Heim >Web-Frontend >js-Tutorial >So erstellen Sie mithilfe von Knoten ein lesbares Flussmuster

So erstellen Sie mithilfe von Knoten ein lesbares Flussmuster

php中世界最好的语言
php中世界最好的语言Original
2018-06-04 09:51:141198Durchsuche

Dieses Mal zeige ich Ihnen, wie Sie mit Node ein lesbares Flussmuster erstellen und welche Vorsichtsmaßnahmen es gibt, um mit Node ein lesbares Flussmuster zu erstellen sehen.

Der lesbare Stream des Knotens basiert auf dem Ereignis

Der Flussmodus des lesbaren Streams hat einen „Schalter“, und jedes Mal, wenn der „Schalter“ ist eingeschaltet. Wenn der Flow-Modus wirksam wird und dieser „Schalter“ auf „Pause“ eingestellt ist, liest der lesbare Stream die Datei nicht, bis der „Schalter“ auf „Flow“ zurückgesetzt wird.

Dateivorgang lesen

Der Vorgang des Lesens des Dateiinhalts besteht hauptsächlich aus:

  1. Öffnen Sie die Wenn die Datei erfolgreich geöffnet wird, wird das Öffnungsereignis ausgelöst. Wenn das Öffnen fehlschlägt, werden das Fehlerereignis und das Schließereignis ausgelöst, um die Datei zu schließen.

  2. Beginnen Sie mit dem Lesen des Inhalts der Datei und hören Sie sich das Datenereignis an. Die Daten befinden sich in einem fließenden Zustand und das Lesen kann durch Ändern des Status des Schalters angehalten werden.

  3. Jedes Mal, wenn der Inhalt gelesen wird, wird er in den Cache gelegt und die Daten werden über das Datenereignis veröffentlicht.

  4. Nachdem Sie den Inhalt der Datei gelesen haben, schließen Sie die Datei.

Diese Reihe von Aktionen basiert alle auf Ereignissen, und wir alle wissen, dass Ereignisse im Knoten in einem Publish-Subscribe-Modell implementiert werden.

Werfen wir einen Blick darauf, wie Node lesbare Streams verwendet, um den Inhalt von Dateien zu lesen?

Knotenlesbare Stream-Parameter

Zuerst erstellen wir einen lesbaren Stream über das fs-Modul. Der lesbare Stream akzeptiert zwei Parameter:

  1. Der erste Parameter ist die Dateiadresse, die gelesen werden soll. Geben Sie hier an, welche Datei Sie lesen möchten.

  2. Der zweite Parameter ist optional. Dieser Parameter ist ein Objekt, das zur Angabe einiger spezifischer Parameter des lesbaren Streams verwendet wird.

Lassen Sie uns die folgenden Parameter einzeln erklären:

  • highWaterMark: Dieser Parameter wird hauptsächlich zum Lesen von Dateien verwendet. Wenn der lesbare Stream den Inhalt der Datei in den Cache liest, müssen wir einen Puffer zum Zwischenspeichern der Daten erstellen. Daher wird dieser Parameter verwendet, um die Größe des Puffers festzulegen. Wenn dieser Parameter nicht festgelegt ist, ist er lesbar Die Standardkonfiguration des Streams ist 64 KB.

  • Flags: Dieser Parameter wird hauptsächlich zum Festlegen des Ausführungsmodus der Datei verwendet. Unsere spezifischen Vorgänge eignen sich beispielsweise zum Lesen von Dateien oder zum Schreiben von Dateien. Wenn wir in eine Datei schreiben, verwenden wir w. Wenn Sie eine Datei lesen, sollte dieser Operator r sein.

Die folgende Tabelle zeigt, dass verschiedene Symbole unterschiedliche Bedeutungen haben:

符号 含义
r 读文件,文件不存在报错
r+ 读取并写入,文件不存在报错
rs 同步读取文件并忽略缓存
w 写入文件,不存在则创建,存在则清空
wx 排它写入文件
w+ 读取并写入文件,不存在则创建,存在则清空
wx+ 和w+类似,排他方式打开
a 追加写入
ax 与a类似,排他方式写入
a+ 读取并追加写入,不存在则创建
ax+ 作用与a+类似,但是以排他方式打开文件
  • autoClose: Dieser Parameter wird hauptsächlich zur Steuerung des Schließens von Dateien verwendet. Wenn beim erneuten Öffnen der Datei oder bei anderen Vorgängen ein Fehler auftritt, muss die Datei geschlossen werden. Dann ist dieser Parameter die Funktion, um festzulegen, ob die Datei automatisch geschlossen wird.

  • Kodierung: Knoten verwendet Puffer, um Binärdaten für Dateivorgänge zu lesen. Wenn diese Daten angezeigt werden, sehen wir eine Menge Kauderwelsch, daher müssen wir ein bestimmtes Codierungsformat für diese Daten angeben. Anschließend werden die Daten kodiert und konvertiert, sodass es sich bei den konvertierten Daten um für uns verständliche Daten handelt.

  • starts: Dieser Parameter wird hauptsächlich verwendet, um die Position anzugeben, an der mit dem Lesen des Dateiinhalts begonnen werden soll. Standardmäßig wird bei Null begonnen.

  • ends: Dieser Parameter wird hauptsächlich verwendet, um anzugeben, wie lange die Daten der Datei gelesen werden sollen. Hier muss erklärt werden, dass dieser Parameter eine eigene Position enthält, nämlich die sogenannte Paketfront Und nach dem Paket.

Sehen wir uns ein konkretes Beispiel eines lesbaren Streams an:

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); });

Der erste Schritt eines handgeschriebenen lesbaren Streams

Wie oben erwähnt, basiert der lesbare Stream des Knotens auf den Kernmodulereignissen des Knotens. Wenn wir also unseren eigenen lesbaren Stream implementieren, müssen wir das Ereignismodul erben. Der Code lautet wie folgt:

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

Durch die Erbung der EventEmitter-Klasse können wir verschiedene Methoden in der EventEmitter-Klasse verwenden und auch den Publish-Subscribe-Modus zum Verarbeiten von Ereignissen verwenden.

Schritt 2: Verarbeiten Sie die Parameter der lesbaren Stream-Konfiguration

Wie oben erwähnt, können Sie dies konfigurieren, wenn Sie einen lesbaren Stream erstellen Knoten Spezifische Parameter der Stream-Konfiguration, wie zum Beispiel

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

Für diese Parameter muss die lesbare Stream-Klasse, die wir selbst implementieren, diese Parameter also auch verarbeiten.

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();
}

Das übliche Konfigurationsprinzip basiert auf den vom Benutzer konfigurierten Parametern. Wenn der Benutzer diesen Parameter nicht festlegt, wird die Standardkonfiguration verwendet.

Der dritte Schritt zum Implementieren eines lesbaren Streams: Öffnen Sie die Datei

Das Prinzip besteht hier darin, die Open-Methode im Knotenmodul fs zu verwenden . Sehen wir uns zunächst die Verwendung der Methode fs.open() an.

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

Es muss hier erklärt werden, es gibt zwei Parameter in der Callback-FunktionCallback:

  1. Der erste ist Fehler und so Bei asynchronen Rückrufen im Knoten wird ein Parameter zurückgegeben, um die spezifische Fehlermeldung zu beschreiben.

  2. Der zweite Parameter ist fd, der Dateideskriptor, der zur Identifizierung der Datei verwendet wird , entspricht dem ersten Parameter der Open-Funktion

Okay, jetzt schauen wir uns an, wie wir die Open-Methode unseres eigenen lesbaren Streams implementieren:

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); // 触发文件的打开的方法
  });
}

From Der Code, den wir oben sehen können:

Die Funktion fs.open ist eine asynchrone Funktion, was bedeutet, dass der Rückruf asynchron ausgeführt wird. Wenn die Datei erfolgreich geöffnet wird, wird auch das fd-Attribut asynchron abgerufen muss beachtet werden.

Ein weiterer wichtiger Punkt ist, dass wenn beim Öffnen der Datei ein Fehler auftritt, dies bedeutet, dass das Öffnen der Datei fehlgeschlagen ist, die Datei zu diesem Zeitpunkt geschlossen werden muss. „Schalter“, wenn wir den Inhalt der Datei lesen möchten, müssen wir diesen „Schalter“ einschalten. Wie schaltet der vom Knoten lesbare Stream selbst diesen „Schalter“ ein?

Datenereignisse abhörenDer vom Knoten lesbare Stream erkennt das Öffnen dieses „Schalters“ durch Abhören von Datenereignissen:

rs.on("data", data => {
  console.log(data);
});

Wenn der Benutzer hört auf das Datenereignis. Wenn der „Schalter“ eingeschaltet ist und der Inhalt kontinuierlich aus der Datei gelesen wird. Wie hört der Knoten Datenereignisse ab?

Die Antwort ist der newListener des Ereignismoduls

Dies liegt daran, dass der vom Knoten lesbare Stream auf Ereignissen basiert und der Server im Ereignisfall alle Ereignisse des Benutzers über das newListener-Ereignis abhören kann. Jedes Ereignis hat entsprechende Typen. Wenn der Benutzer das Datenereignis abhört, können wir es abrufen und dann den Inhalt in der Datei lesen.

// 监听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实做出加密解密操作

Das obige ist der detaillierte Inhalt vonSo erstellen Sie mithilfe von Knoten ein lesbares Flussmuster. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn