Maison  >  Article  >  interface Web  >  Comment utiliser Node pour créer un modèle de flux lisible

Comment utiliser Node pour créer un modèle de flux lisible

php中世界最好的语言
php中世界最好的语言original
2018-06-04 09:51:141101parcourir

Cette fois, je vais vous montrer comment utiliser node pour créer un modèle de flux lisible, et quelles sont les précautions pour utiliser node pour créer un modèle de flux lisible. Voici un cas pratique, prenons un. regarder.

Le flux lisible du nœud est basé sur l'événement

Le mode de flux du flux lisible Ce mode de flux aura un "switch", et à chaque fois le. Le "switch" est activé Lorsque le mode flux prend effet, si ce "switch" est réglé sur pause, alors le flux lisible ne lisera pas le fichier jusqu'à ce que le "switch" soit réinitialisé sur flow.

Processus de lecture du fichier

Le processus de lecture du contenu du fichier est principalement :

  1. Ouvrir le Si le fichier est ouvert avec succès, l'événement d'ouverture sera déclenché. Si l'ouverture échoue, l'événement d'erreur et l'événement de fermeture seront déclenchés pour fermer le fichier.

  2. Commencez à lire le contenu du fichier et écoutez l'événement de données. Les données sont dans un état fluide et la lecture peut être interrompue en modifiant l'état du commutateur.

  3. Chaque fois que le contenu est lu, il est mis dans le cache et les données sont publiées via l'événement data.

  4. Une fois le contenu du fichier lu, fermez le fichier.

Cette série d'actions est toutes basée sur des événements, et nous savons tous que les événements dans le nœud sont implémentés dans un modèle de publication-abonnement.

Voyons comment le nœud utilise les flux lisibles pour lire le contenu des fichiers ?

Paramètres du flux lisible par nœud

Nous créons d'abord un flux lisible via le module fs. Le flux lisible accepte deux paramètres :

<.>
  1. Le premier paramètre est l'adresse du fichier à lire, précisez ici quel fichier vous souhaitez lire.

  2. Le deuxième paramètre est facultatif. Ce paramètre est un objet utilisé pour spécifier certains paramètres spécifiques du flux lisible.

Expliquons les paramètres suivants un par un :

  • highWaterMark : Définissez la limite supérieure d'eau. Ce paramètre est principalement utilisé pour lire les fichiers. Lorsque, le flux lisible lira le contenu du fichier dans le cache, et ici nous devons créer un tampon pour mettre en cache les données, ce paramètre est donc utilisé pour définir la taille du tampon. Si ce paramètre n'est pas défini, lisible. La configuration par défaut du flux est 64k.

  • flags : Ce paramètre est principalement utilisé pour définir le mode d'exécution du fichier. Par exemple, nos opérations spécifiques conviennent à la lecture de fichiers ou à l'écriture de fichiers. Si nous écrivons dans un fichier, nous utilisons w. Si vous lisez un fichier, cet opérateur doit être r.

Le tableau suivant montre que différents symboles représentent différentes significations :

  • autoClose : Ce paramètre est principalement utilisé pour contrôler la fermeture des fichiers. Si une erreur se produit lors de la réouverture du fichier ou d'autres opérations, le fichier doit être fermé. Ensuite, ce paramètre est la fonction permettant de définir si le fichier est automatiquement fermé.

  • encodage : le nœud utilise un tampon pour lire les données binaires pour les opérations sur les fichiers. Lorsque ces données seront affichées, nous verrons un tas de charabia, nous devons donc spécifier un format de codage spécifique pour ces données. Les données seront ensuite codées et converties afin que les données converties soient des données que nous pouvons comprendre.

  • starts : Ce paramètre est principalement utilisé pour spécifier la position à partir de laquelle commencer la lecture du contenu du fichier. La valeur par défaut est de partir de zéro.

  • fins : Ce paramètre est principalement utilisé pour spécifier la durée pendant laquelle les données du fichier doivent être lues. Il faut expliquer ici que ce paramètre inclut sa propre position, qui est la. ce qu'on appelle le devant du colis et après le colis.

Jetons un coup d'œil à un exemple spécifique de flux lisible :

let fs = require("fs");
let rs = fs.createReadStream("./a.js", {
  highWaterMark: 3,
  encoding: "utf8",
  autoClose: true,
  start: 0,
  end: 9
});
rs.on("open", () => {console.log("open");});
rs.on("close", () => {console.log("close");});
rs.on("data", data => {
  console.log(data);
  rs.pause();//暂停读取 此时流动模式为暂停模式
});
setInterval(() => {
  rs.resume();//重新设置为流动模式,开始读取数据
}, 1000);
rs.on("end", () => { console.log("end"); });
rs.on("error", err => { console.log(err); });

La première étape d'un flux lisible manuscrit

Comme nous l'avons dit ci-dessus, le flux lisible du nœud est basé sur les événements du module principal du nœud, donc lors de l'implémentation de notre propre flux lisible, nous devons hériter du module d'événements. Le code est le suivant. suit :

let fs = require('fs');
let EventEmitter = require('events');
class ReadStream extends EventEmitter {
}

En héritant de la classe EventEmitter, nous pouvons utiliser diverses méthodes dans la classe EventEmitter, et également utiliser le modèle de publication-abonnement pour traiter les événements.

Étape 2 : Traiter les paramètres de la configuration du flux lisible

Comme nous l'avons mentionné ci-dessus, vous pouvez configurer cela lors de la création d'un flux lisible dans node Les paramètres spécifiques de la configuration du flux, tels que

let rs = fs.createReadStream("./a.js", {
  highWaterMark: 3,
  encoding: "utf8",
  autoClose: true,
  start: 0,
  end: 9
});

, puis pour ces paramètres, la classe de flux lisible que nous implémentons nous-mêmes doit également traiter ces paramètres, alors comment ces paramètres doivent-ils être traités ?

constructor(path, options = {}) {
  super();
  this.path = path; //指定要读取的文件地址
  this.highWaterMark = options.highWaterMark || 64 * 1024;
  this.autoClose = options.autoClose || true; //是否自动关闭文件
  this.start = options.start || 0; //从文件哪个位置开始读取
  this.pos = this.start; // pos会随着读取的位置改变
  this.end = options.end || null; // null表示没传递
  this.encoding = options.encoding || null;// buffer编码
  this.flags = options.flags || 'r';
  this.flowing = null; // 模式开关
  this.buffer = Buffer.alloc(this.highWaterMark);// 根据设置创建一个buffer存储读出来的数
  this.open();
}

Le principe de configuration habituel est basé sur les paramètres configurés par l'utilisateur. Si l'utilisateur ne définit pas ce paramètre, la configuration par défaut sera utilisée.

La troisième étape pour implémenter un flux lisible : Ouvrir le fichier

Le principe ici est d'utiliser la méthode open dans le module node fs . Tout d’abord, passons en revue l’utilisation de la méthode fs.open().

fs.open(filename,flags,[mode],callback);
//实例
fs.open('./1,txt','r',function(err,fd){});

Cela doit être expliqué ici, Fonction de rappel Il y a 2 paramètres dans le rappel :

  1. Le premier est l'erreur, qui est asynchrone dans le nœud Le rappel renverra un paramètre pour décrire le message d'erreur spécifique

  2. Le deuxième paramètre est fd, qui est le descripteur de fichier, utilisé pour identifier le fichier, etc. La valeur est le premier paramètre de la fonction open

Bien, voyons maintenant comment implémenter la méthode open de notre propre flux lisible :

open() {
  fs.open(this.path, this.flags, (err, fd) => { 
    //fd标识的就是当前this.path这个文件,从3开始(number类型)
    if (err) {
      if (this.autoClose) { // 如果需要自动关闭则去关闭文件
        this.destroy(); // 销毁(关闭文件,触发关闭事件)
      }
      this.emit('error', err); // 如果有错误触发error事件
      return;
    }
    this.fd = fd; // 保存文件描述符
    this.emit('open', this.fd); // 触发文件的打开的方法
  });
}

Nous pouvons voir dans le code :

La fonction fs.open est une fonction asynchrone, ce qui signifie que le rappel est exécuté de manière asynchrone. Lorsque le fichier est ouvert avec succès, l'attribut fd est également obtenu. de manière asynchrone. Cela nécessite une attention particulière.

Un autre point important est que si une erreur se produit lors de l'ouverture du fichier, cela signifie que l'ouverture du fichier a échoué et que le fichier doit être fermé à ce moment-là. " switch ", lorsque nous voulons lire le contenu du fichier, nous devons activer ce " switch ", alors comment le flux lisible par le nœud lui-même active-t-il ce " switch " ?

Écouter les événements de donnéesLe flux lisible par nœud réalise l'ouverture de ce "switch" en écoutant les événements de données :

Quand l'utilisateur écoute Lorsque l'événement de données se produit, le "commutateur" est activé et le contenu est lu en continu à partir du fichier. Alors, comment le nœud écoute-t-il les événements de données ?

La réponse est le newListener du module d'événementEn effet, le flux lisible par le nœud est basé sur des événements, et dans l'événement, le serveur peut écouter tous les événements de l'utilisateur via l'événement newListener. Chaque événement a des types correspondants. Lorsque l'utilisateur écoute l'événement de données, nous pouvons l'obtenir puis lire le contenu du fichier. Alors, comment pouvons-nous implémenter notre propre flux lisible ?

// 监听newListener事件,看是否监听了data事件,如果监听了data事件的话,就开始启动流动模式,读取文件中的内容
this.on("newListener", type => {
  if (type === "data") {
    // 开启流动模式,开始读取文件中的内容
    this.flowing = true;
    this.read();
  }
});

好了,知道了这个"开关"是如何打开的,那么这个时候就到了真正读取文件中内容的关键时候了,先上代码先:

read() {
  // 第一次读取文件的话,有可能文件是还没有打开的,此时this.fd可能还没有值
  if (typeof this.fd !== "number") {
    // 如果此时文件还是没有打开的话,就触发一次open事件,这样文件就真的打开了,然后再读取
    return this.once("open", () => this.read());
  }
  // 具体每次读取多少个字符,需要进行计算,因为最后一次读取倒的可能比highWaterMark小
  let howMuchRead = this.end ? Math.min(this.end - this.pos + 1, this.highWaterMark) : this.highWaterMark;
  fs.read(this.fd, this.buffer, 0, howMuchRead, this.pos, (err, byteRead) => {
    // this.pos 是每次读取文件读取的位置,是一个偏移量,每次读取会发生变化
    this.pos += byteRead;
    // 将读取到的内容转换成字符串串,然后通过data事件,将内容发布出去
    let srr = this.encoding ? this.buffer.slice(0, byteRead).toString(this.encoding) : this.buffer.slice(0, byteRead);
    // 将内容通过data事件发布出去
    this.emit("data", srr);
    // 当读取到到内容长度和设置的highWaterMark一致的话,并且还是流动模式的话,就继续读取
    if ((byteRead === this.highWaterMark) && this.flowing) {
      return this.read();
    }
    // 没有更多的内容了,此时表示文件中的内容已经读取完毕
    if (byteRead < this.highWaterMark) {
      // 读取完成,发布end方法,并关闭文件
      this.emit("end");
      this.destory();
    }
  });
}

这里我们特别要注意的是:

  1. 文件是否已经打开,是否获取到fd,如果没有打开的话,则再次触发open方法

  2. 分批次读取文件内容,每次读取的内容是变化的,所以位置和偏移量是要动态计算的

  3. 控制读取停止的条件。

实现可读流第五步:关闭文件

好了,到现在,基础的读取工作已经完成,那么就需要将文件关闭了,上面的open和read方法里面都调用了一个方法:destory,没错,这个就是关闭文件的方法,好了,那么我们来看看这个方法该如何实现吧

destory() {
  if (typeof this.fd !== "number") {
    // 发布close事件
    return this.emit("close");
  }
  // 将文件关闭,发布close事件
  fs.close(this.fd, () => {
    this.emit("close");
  });
}

当然这块的原理就是调用fs模块的close方法啦。

实现可读流第六步:暂停和恢复

既然都说了,node可读流有一个神奇的"开关",就像大坝的阀门一样,可以控制水的流动,同样也可以控制水的暂停啦。当然在node可读流中的暂停是停止对文件的读取,恢复就是将开关打开,继续读取文件内容,那么这两个分别对应的方法就是pause()和resume()方法。

那么我们自己的可读流类里面该如何实现这两个方法的功能呢?非常简单:

我们在定义类的私有属性的时候,定义了这样一个属性flowing,当它的值为true时表示开关打开,反之关闭。

pause() {
  this.flowing = false;// 将流动模式设置成暂停模式,不会读取文件
}
resume() {
  this.flowing = true;//将模式设置成流动模式,可以读取文件
  this.read();// 重新开始读取文件
}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

怎样利用JS做出引用传递与值传递

使用JS实做出加密解密操作

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn