在node.js中fs.createReadStream与fs.readFile的优缺点是什么?

Ken*_*ter 71 javascript file fs node.js

我正在讨论node.js,并且已经发现了两种读取文件并将其发送到网络的方法,一旦我确定它存在并且已经使用writeHead发送了正确的MIME类型:

// read the entire file into memory and then spit it out

fs.readFile(filename, function(err, data){
  if (err) throw err;
  response.write(data, 'utf8');
  response.end();
});

// read and pass the file as a stream of chunks

fs.createReadStream(filename, {
  'flags': 'r',
  'encoding': 'binary',
  'mode': 0666,
  'bufferSize': 4 * 1024
}).addListener( "data", function(chunk) {
  response.write(chunk, 'binary');
}).addListener( "close",function() {
  response.end();
});
Run Code Online (Sandbox Code Playgroud)

我是否正确假设fs.createReadStream可能提供更好的用户体验,如果有问题的文件是大的,如视频?感觉它可能不那么块状; 这是真的?我还需要了解其他优缺点,缺点,警告或陷阱吗?

isa*_*acs 57

A better approach, if you are just going to hook up "data" to "write()" and "close" to "end()":

// 0.3.x style
fs.createReadStream(filename, {
  'bufferSize': 4 * 1024
}).pipe(response)

// 0.2.x style
sys.pump(fs.createReadStream(filename, {
  'bufferSize': 4 * 1024
}), response)
Run Code Online (Sandbox Code Playgroud)

The read.pipe(write) or sys.pump(read, write) approach has the benefit of also adding flow control. So, if the write stream cannot accept data as quickly, it'll tell the read stream to back off, so as to minimize the amount of data getting buffered in memory.

The flags:"r" and mode:0666 are implied by the fact that it is a FileReadStream. The binary encoding is deprecated -- if an encoding is not specified, it'll just work with the raw data buffers.

Also, you could add some other goodies that will make your file serving a whole lot slicker:

  1. 嗅探req.headers.range并查看它是否与字符串匹配/bytes=([0-9]+)-([0-9]+)/.如果是这样,您只想从该开始到结束位置进行流式传输.(缺少数字表示0或"结束".)
  2. 将inode和创建时间从stat()调用散列到ETag标头中.如果您获得了与该标题匹配的"if-none-match"的请求标头,请发回一个304 Not Modified.
  3. 根据stat对象上的日期检查if-modified-since标题mtime.304如果自提供日期以来未被修改.

另外,一般情况下,如果可以,请发送Content-Length标题.(你是stat这个文件,所以你应该有这个.)

  • 这甚至如何回答最初提出的问题? (4认同)

Chr*_*rey 39

fs.readFile将指出的整个文件加载到内存中,同时fs.createReadStream将以您指定的大小的块读取文件.

客户端也将开始更快地接收数据fs.createReadStream,因为它在读取时以块的形式发送,而fs.readFile将读出整个文件,然后才开始将其发送到客户端.这可能是微不足道的,但如果文件非常大且磁盘速度很慢,则会产生影响.

考虑一下,如果你在100MB文件上运行这两个函数,第一个将使用100MB内存来加载文件,而后者只使用最多4KB.

编辑:我真的没有看到你使用fs.readFile特别的原因,因为你说你将打开大文件.


Dee*_*ohn 6

如果它是一个大文件,那么“readFile”会占用内存,因为它会缓冲内存中的所有文件内容,并可能会挂起您的系统。而ReadStream则分块读取。

运行此代码并观察任务管理器性能选项卡中的内存使用情况。

 var fs = require('fs');

const file = fs.createWriteStream('./big_file');


for(let i=0; i<= 1000000000; i++) {
  file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n');
}

file.end();


//..............
fs.readFile('./big_file', (err, data) => {
  if (err) throw err;
  console.log("done !!");
});
Run Code Online (Sandbox Code Playgroud)

事实上,你不会看到“完成!!” 信息。“readFile”将无法读取文件内容,因为缓冲区不够大,无法容纳文件内容。

现在,使用 readStream 代替“readFile”并监视内存使用情况。

注意:代码取自 Pluralsight 上的 Samer Buna Node 课程