ホームページ  >  記事  >  ウェブフロントエンド  >  Nodejs が大きなファイルを読み書きする方法の簡単な分析

Nodejs が大きなファイルを読み書きする方法の簡単な分析

青灯夜游
青灯夜游オリジナル
2022-09-28 20:09:282132ブラウズ

Nodejs が大きなファイルを読み書きする方法の簡単な分析

作者は最近、node でファイルの読み取り、書き込み、およびマルチパートのアップロード作業を行っています。このプロセス中に、ファイルが次によって読み取られた場合、ノードが 2G を超えると、2G を超えます。Blob の最大値を読み取るために、読み取り例外が発生します。また、ノード内のファイルの読み書きもサーバー RAM によって制限されます。読み込む必要があります。発生した問題とその解決方法を記録します。 [関連チュートリアルの推奨事項: nodejs ビデオ チュートリアル ]

  • ノードでのファイルの読み取りと書き込み
  • ノード ファイルの読み取りと書き込み RAM および BLOB サイズの制限
  • その他

1. ノードでのファイルの読み取りと書き込み

1.1 通常のファイルの読み取りと書き込み

通常、比較的小さなファイルを読み取りたい場合は、次のように直接渡すことができます:

const fs = require('fs')
let data = fs.readFileSync("./test.png")
console.log(data,123)
//输出data = <Buffer 89 50 4e ...>

一般的に、js/nodejs はシングルスレッドであるため、同期メソッドは推奨されません。メインスレッドをブロックします。最新バージョンのノードは fs.promise を直接提供しており、async/await と直接組み合わせて使用​​できます。

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 ...>

ここでの非同期メソッド呼び出しはメインスレッドをブロックせず、複数のファイル読み取りの IO がブロックされる可能性があります。等も並行して行われます。

1.2 ストリーム ファイルの読み取りと書き込み

従来のファイルの読み取りと書き込みでは、ファイルを一度にメモリに読み取ります。この方法は時間とメモリの効率が高くなります。効率的です。両方とも非常に低いです。時間効率が低いということは、実行前にファイルを 1 回読み取る必要があることを意味します。メモリ効率が低いということは、ファイルを一度に読み取ってメモリに配置する必要があり、大量のメモリを消費することを意味します。したがって、この場合、通常は Stream を使用してファイルを読み取ります。

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 ...>

Steam を介してファイルを読み書きすると、メモリ効率と時間効率が向上します。

  • メモリ効率: データを処理する前に大量 (またはデータ全体) をメモリにロードする必要はありません
  • 時間効率: データを取得したら、すぐに開始できますデータ全体がロードされるまで待つのではなく、データの処理を開始するのにかかる時間を大幅に短縮します。

ストリーム ファイルは 2 番目の書き込み方法もサポートしています:

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. ノード ファイルの読み取りおよび書き込み RAM および Blob サイズの制限

2.1 基本的な質問

大きなファイルを読み取る場合、読み取りファイルのサイズに制限があります。たとえば、現在 2.5G ビデオ ファイルを読み取り中です:

const fs = require('fs')
const readFileTest = async () => {
    let data = await fs.promises.readFile("./video.mp4")
    console.log(data)
}
readFileTest()

上記のコードを実行するとエラーが報告されます:

RangeError [ERR_FS_FILE_TOO_LARGE]: ファイル サイズ (2246121911) は 2 GB を超えています

オプション NODE_OPTIONS='- を設定すると考えられるかもしれません。 -max -old-space-size=5000'、この時点では 5000M>2.5G ですが、エラーは依然として消えません。これは、ノードによって読み取られるファイルのサイズ制限をオプションで変更できないことを意味します。

上記は大容量ファイルを読み込む従来の方法ですが、Steam 経由で読み込む場合、ファイルサイズの制限はありますか?例:

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

上記の方法で 2.5G ファイルを読み取る場合は例外はありませんが、ここでエラーが発生することに注意してください:

data += chunk;
                ^

RangeError: Invalid string length

これは、データが最大制限 (2048M など) を超えています。そのため、Steamで処理する場合、読み込み結果を保存する際は、デフォルトのBufferの最大値を超えないようファイルサイズに注意してください。上記の場合、すべてのデータを 1 つの大きなデータに保存するための data = chunk は必要なく、読み込みと処理を同時に行うことができます。

2.2 分割読み取り

ファイルの読み取りプロセス中に、createReadStream は実際にセグメントを読み取ることができます。この分割読み取り方法も使用できます。大きなファイルの読み取り。特に同時読み取りの場合、特定の利点があり、ファイルの読み取りと処理の速度が向上します。

createReadStream は 2 番目のパラメータ {start, end} を受け入れます。 fs.promises.stat を通じてファイルのサイズを取得し、次にフラグメントを決定し、最後にフラグメントを 1 回読み取ることができます。例:

  1. ファイル サイズの取得
const info = await fs.promises.stat(filepath)
   const size = info.size
  1. 指定された SIZE に従ってフラグメント (フラグメントあたり 128M など)
  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. 読み取り関数を実装します

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', () => {
      ...
    })
}

fs.createReadStream (filepath,{ start, end})、開始と終了は前後で閉じられます。たとえば、 fs.createReadSteam(filepath,{start:0,end:1023}) は [0,1023] を読み取り、合計 1024 ビットになります。

3. その他

3.1 ブラウザ側での大きなファイルの読み書きの拡張

大きなファイルは以前はnodejsに保存されていたので、ブラウザ側で大きなファイルを読み取ることに問題はありますか?

    浏览器在本地读取大文件时,之前有类似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 教程

以上がNodejs が大きなファイルを読み書きする方法の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。