Heim  >  Artikel  >  Web-Frontend  >  Eine kurze Analyse, wie Nodejs große Dateien liest und schreibt

Eine kurze Analyse, wie Nodejs große Dateien liest und schreibt

青灯夜游
青灯夜游Original
2022-09-28 20:09:282058Durchsuche

Eine kurze Analyse, wie Nodejs große Dateien liest und schreibt

Der Autor hat kürzlich auf der Knotenseite einige Dateien gelesen, geschrieben und Shards hochgeladen. Während dieses Vorgangs habe ich festgestellt, dass die vom Knoten gelesene Datei 2G überschreitet und den maximalen Lese-Blob überschreitet Es tritt eine Ausnahme auf. Darüber hinaus unterliegt das Lesen und Schreiben von Dateien im Knoten auch den RAM-Einschränkungen des Servers und muss in Abschnitten gelesen werden. Ich werde die aufgetretenen Probleme und deren Lösung aufzeichnen. [Empfohlene verwandte Tutorials: nodejs-Video-Tutorial]

    Lesen und Schreiben von Dateien im Knoten
  • Lesen und Schreiben von Knotendateien, RAM- und Blob-Größenbeschränkungen
  • Sonstige

1. Lesen und Schreiben von Dateien im Knoten

1.1 Regelmäßiges Lesen und Schreiben von Dateien

Wenn wir regelmäßig eine relativ kleine Datei lesen möchten, können wir Folgendes direkt übergeben:

const fs = require('fs')
let data = fs.readFileSync("./test.png")
console.log(data,123)
//输出data = <Buffer 89 50 4e ...>
Im Allgemeinen ist die Synchronisierungsmethode nicht sehr zu empfehlen, da js/nodejs Single- threaded Ja, die synchronisierte Methode blockiert den Hauptthread. Die neueste Version von node stellt fs.promise direkt bereit, das direkt in Kombination mit async/await verwendet werden kann:

const fs = require('fs')
const readFileSync = async () => {
    let data = await fs.promises.readFile("./test.png")
    console.log(data,123)
}
readFileSync()
//输出data = <Buffer 89 50 4e ...>
Der asynchrone Methodenaufruf hier blockiert nicht den Hauptthread, und die E/A mehrerer Dateilesungen kann auch in ausgeführt werden parallel.

1.2 Lesen und Schreiben von Stream-Dateien

Beim herkömmlichen Lesen und Schreiben von Dateien lesen wir die Datei auf einmal in den Speicher ein, und eine geringe Zeiteffizienz bedeutet, dass dies der Fall sein muss einmalig ausgeführt Die nachfolgende Ausführung kann erst nach Abschluss des ersten Lesevorgangs erfolgen. Eine geringe Speichereffizienz bedeutet, dass die Datei sofort gelesen und in den Speicher gestellt werden muss, was viel Speicher beansprucht. Daher verwenden wir in diesem Fall im Allgemeinen Stream zum Lesen von Dateien:

const fs = require('fs')
const readFileTest = () => {
    var data = ''
    var rs = fs.createReadStream('./test.png');
    rs.on('data', function(chunk) {
        data += chunk;
        console.log(chunk)
     });
    rs.on('end',function(){
        console.log(data);
    });
    rs.on('error', function(err){
        console.log(err.stack);
     });
}
readFileTest()
// data = <Buffer 89 50 64 ...>
Das Lesen und Schreiben von Dateien über Steam kann die Speichereffizienz und Zeiteffizienz verbessern.

    Speichereffizienz: Es ist nicht erforderlich, eine große Menge (oder die gesamten) Daten vor der Verarbeitung in den Speicher zu laden.
  • Zeiteffizienz: Sobald Sie die Daten haben, können Sie mit der Verarbeitung beginnen, was die Zeit bis zum Beginn der Verarbeitung erheblich verkürzt die Daten, ohne warten zu müssen. Die gesamten Daten werden vor der Verarbeitung geladen.
Stream-Dateien unterstützen auch die zweite Schreibmethode:

const fs = require('fs')
const readFileTest = () => {
    var data = ''
    var chunk;
    var rs = fs.createReadStream('./test.png');
    rs.on('readable', function() {
    while ((chunk=rs.read()) != null) {
        data += chunk;
    }});
    rs.on('end', function() {
        console.log(data)
    });
};
readFileTest()

2. Einschränkungen beim Lesen und Schreiben von Knotendateien, RAM und Blob-Größe

2.1 Grundlegende Probleme

Beim Lesen großer Dateien wird die Dateigröße gelesen Grenze, zum Beispiel lesen wir gerade eine 2,5G-Videodatei:

const fs = require('fs')
const readFileTest = async () => {
    let data = await fs.promises.readFile("./video.mp4")
    console.log(data)
}
readFileTest()
Beim Ausführen des obigen Codes wird ein Fehler gemeldet:

RangeError [ERR_FS_FILE_TOO_LARGE]: Dateigröße (2246121911) ist größer als 2 GB

Vielleicht dachte ich dass durch Festlegen der Option NODE_OPTIONS='--max-old-space-size=5000' zu diesem Zeitpunkt 5000M>2,5G, der Fehler jedoch immer noch nicht verschwunden ist, was bedeutet, dass die Größenbeschränkung der Knotenlesedateien nicht geändert werden kann über Optionen.

Das Obige ist eine herkömmliche Methode zum Lesen großer Dateien. Gibt es eine Dateigrößenbeschränkung, wenn sie über Steam gelesen wird? Zum Beispiel:

const fs = require('fs')
const readFileTest = () => {
    var data = ''
    var rs = fs.createReadStream('./video.mp4');
    rs.on('data', function(chunk) {
        data += chunk;
     });
    rs.on('end',function(){
        console.log(data);
    });
    rs.on('error', function(err){
        console.log(err.stack);
     });
}
readFileTest()
Beim Lesen einer 2,5G-Datei auf die oben beschriebene Weise wird es keine Ausnahme geben, aber bitte beachten Sie, dass hier ein Fehler vorliegt:

data += chunk;
                ^

RangeError: Invalid string length
Dies liegt daran, dass die Länge der Daten die maximale Grenze überschreitet, z. B. 2048 MB , usw. Achten Sie daher bei der Verarbeitung mit Steam beim Speichern der Leseergebnisse auf die Dateigröße, die den standardmäßigen Maximalwert des Puffers nicht überschreiten darf. Im obigen Fall benötigen wir keinen Datenblock, um alle Daten in einer großen Datenmenge zu speichern. Wir können sie gleichzeitig lesen und verarbeiten.

2.2 Segmentiertes Lesen

Beim Lesen von Dateien kann createReadStream tatsächlich Segmente einlesen. Diese segmentierte Lesemethode kann alternativ auch zum Lesen großer Dateien verwendet werden. Insbesondere beim gleichzeitigen Lesen bietet es bestimmte Vorteile und kann die Geschwindigkeit des Lesens und Verarbeitens von Dateien verbessern.

CreateReadStream akzeptiert den zweiten Parameter {start, end}. Wir können die Größe der Datei über fs.promises.stat ermitteln, dann die Fragmente bestimmen und das letzte Fragment auf einmal lesen, zum Beispiel:

    Ermitteln Sie die Dateigröße
  1. const info = await fs.promises.stat(filepath)
       const size = info.size
    Fragment entsprechend der angegebenen GRÖSSE (
  1.   const SIZE = 128 * 1024 * 1024
      let sizeLen = Math.floor(size/SIZE)
        let total = sizeLen +1 ;
        for(let i=0;i<=sizeLen;i++){
          if(sizeLen ===i){
            console.log(i*SIZE,size,total,123)
            readStremfunc(i*SIZE,size,total)
          }else{
            console.log(i*SIZE,(i+1)*SIZE,total,456)
            readStremfunc(i*SIZE,(i+1)*SIZE-1,total)
          }
        }
      //分片后【0,128M】,【128M, 256M】...
3. Implementieren Sie die Lesefunktion

const readStremfunc = () => {
    const readStream =  fs.createReadStream(filepath,{start:start,end:end})
    readStream.setEncoding('binary')
    let data = ''
    readStream.on('data', chunk => {
        data = data + chunk
    })
    readStream.end('data', () => {
      ...
    })
}
Es ​​ist erwähnenswert, dass fs.createReadStream(filepath,{start,end}), start und end vorne und hinten geschlossen sind, wie z fs.createReadSteam(filepath,{ start:0,end:1023}) liest [0,1023], insgesamt 1024 Bits.

3. Sonstiges

3.1 Erweitern Sie das Lesen und Schreiben großer Dateien auf der Browserseite.

Wir haben bereits große Dateien in NodeJS gelesen. Gibt es also Probleme beim Lesen großer Dateien auf der Browserseite?

    浏览器在本地读取大文件时,之前有类似FileSaver、StreamSaver等方案,不过在浏览器本身添加了File的规范,使得浏览器本身就默认和优化了Stream的读取。我们不需要做额外的工作,相关的工作:github.com/whatwg/fs。不过不同的版本会有兼容性的问题,我们还是可以通过FileSaver等进行兼容。

3.2 请求静态资源大文件

    如果是在浏览器中获取静态资源大文件,一般情况下只需要通过range分配请求即可,一般的CDN加速域名,不管是阿里云还是腾讯云,对于分片请求都支持的很好,我们可以将资源通过cdn加速,然后在浏览器端直接请求cdn加速有的资源。

    分片获取cdn静态资源大文件的步骤为,首先通过head请求获取文件大小:

const getHeaderInfo = async (url: string) => {
  const res: any = await axios.head(url + `?${Math.random()}`);
  return res?.headers;
};
const header = getHeaderInfo(source_url)
const size = header['content-length']

我们可以从header中的content-length属性中,获取文件的大小。然后进行分片和分段,最后发起range请求:

const getRangeInfo = async (url: string, start: number, end: number) => {
    const data = await axios({
      method: 'get',
      url,
      headers: {
        range: `bytes=${start}-${end}`,
      },
      responseType: 'blob',
    });
    return data?.data;
  };

在headers中指定 range: bytes=${start}-${end},就可以发起分片请求去获取分段资源,这里的start和end也是前闭后闭的。

更多node相关知识,请访问:nodejs 教程

Das obige ist der detaillierte Inhalt vonEine kurze Analyse, wie Nodejs große Dateien liest und schreibt. 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