2018.08.15 11:42

Node处理大文件的Stream流的高级用法

Node处理大文件的Stream流的高级用法1



fs.readFile 这个在处理大文件的时候不能够很好的胜任工作,导致读取文件失败


这种方法简单易理解,但有个最大的缺点,就是你读取的文件有多大,合并的过程占用的内存就有多大,因为我们相当于把这个大文件的全部内容都一次性载入到内存中了,

这是非常低效的。同时,Node默认的缓冲区大小的上限是2GB,一旦我们上传的大文件超出2GB,那使用这种方法就会失败。虽然可以通过修改缓冲区大小上限的方法来规避这个问题,

但是鉴于这种合并方式极吃内存,我不建议您这么做。


那么,有更好的方式吗?那是当然,下面介绍一种stream合并方式。


在Node.js中,我们可以通过两种方式来读取文件:


使用fs.readFile()一次性将文件内容全部读取出来,考虑到可能将来会操作几G大的文件,所以放弃了这种方式;

使用fs.createReadStream()创建一个读文件流,这种方式可不受限于文件的大小;

因此,我很顺理成章地选用了fs.createReadStream()来读取文件,自然在写文件时也使用对应的fs.createWriteStream()来做。



// 合并分片 fs readFileSync
  function mergeChunks(fileName, chunks, callback) {
    console.log('chunks:' + chunks);
    let chunkPaths = chunks.map(function (name) {
      return path.join(process.env.IMAGESDIR, name)
    });

    // 采用Buffer方式合并
    const readStream = function (chunkArray, cb) {
      let buffers = [];
      chunkPaths.forEach(function (path) {
        let buffer = fs.readFileSync(path);
        buffers.push(buffer);
      });

      let concatBuffer = Buffer.concat(buffers);
      let concatFilePath = path.join(process.env.IMAGESDIR, fileName);
      fs.writeFileSync(concatFilePath, concatBuffer);

      chunkPaths.forEach(function (path) {
        fs.unlinkSync(path)
      })
      cb();
    };


    readStream(chunkPaths, callback);

  }

优化

// 合并分片
  function mergeChunks(fileName, chunks, callback) {
    console.log('chunks:' + chunks);
    let chunkPaths = chunks.map(function (name) {
      return path.join(process.env.IMAGESDIR, name)
    });

    // 采用Stream方式合并
    let targetStream = fs.createWriteStream(path.join(process.env.IMAGESDIR, fileName));
    const readStream = function (chunkArray, cb) {
      let path = chunkArray.shift();
      let originStream = fs.createReadStream(path);
      originStream.pipe(targetStream, {end: false});
      originStream.on("end", function () {
        // 删除文件
        fs.unlinkSync(path);
        if (chunkArray.length > 0) {
          readStream(chunkArray, callback)
        } else {
          cb()
        }
      });
    };

    readStream(chunkPaths, callback);

  }



另外一种:


//way1 http
// const fs = require('fs');
// const server = require('http').createServer();

// server.on('request', (req, res) => {
//  const src = fs.createReadStream('./big.txt');
//  src.pipe(res);
// });

// server.listen(8000);



// 你可以将这个例子推到极限。重新生成big.txt500万行,而不仅仅是100万行,这会使文件超过2 GB,实际上它大于Node中的默认缓冲区限制。

// 如果您尝试使用该文件fs.readFile,则默认情况下您不能(您可以更改限制)。但是fs.createReadStream,在向请求者传输2GB数据的过程中没有任何问题,最重要的是,进程内存使用率将大致相同。
// 
// Administrator@lenovo-PC MINGW64 /e/jackieli/longyuanWork/longyuan-ai-pc (test)
// $ curl localhost:8000

// Administrator@lenovo-PC MINGW64 /e/jackieli/node-code-write-练习/// $ node readStream.js


// ## way2 express

const express = require('express');
const fs = require('fs');
let app = express();
// app.use('/', express.static(__dirname + '/app/'));

// app.get('/', function (req, res) {
//  res.sendFile(__dirname + '/app/index.html');
// });

app.get('/', function (req, res, next) {
  //way1	
 // res.writeHead(200, {
 //  'Content-Type': 'text/event-stream',
 //  'Cache-Control': 'no-cache',
 //  'Connection': 'keep-alive'
 // });
 // const src = fs.createReadStream("./big.txt");
 // src.pipe(res);
  
 //way2
 fs.createReadStream('./big.txt').pipe(res);
  //fs.createReadStream('./big.txt').pipe(process.stdout);
  
});

app.listen(8000, () => console.log('booting server...'));

DIY表情

(添加http或https协议)

提交评论