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


74

我正在弄乱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();
});

如果所讨论的文件很大,例如视频,fs.createReadStream可能会提供更好的用户体验,我是否正确?感觉好像不那么块状。这是真的?我还需要了解其他优点,缺点,警告或陷阱吗?

Answers:


59

如果您只是将“数据”连接到“ write()”,将“关闭”连接到“ 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)

read.pipe(write)或”sys.pump(read, write)方法的好处还在于增加了流量控制。因此,如果写入流不能尽快接受数据,它将告诉读取流退回,以最大程度地减少缓冲在内存中的数据量。

flags:"r"mode:0666由事实,这是一个暗示FileReadStream。该binary编码已过时-如果没有指定编码,它会刚刚与原始数据缓冲区的工作。

此外,您还可以添加其他一些功能,使您的文件更加流畅:

  1. 嗅探req.headers.range一下,看是否匹配字符串/bytes=([0-9]+)-([0-9]+)/。如果是这样,您只想从该起点到终点流。(缺少数字表示0或“结尾”。)
  2. 将stat()调用中的inode和创建时间散列到ETag头中。如果您收到的请求标头具有“ if-none-match”匹配的标头,请发送回304 Not Modified
  3. 根据stat对象上的日期检查if-modified-since标题mtime。304(如果自提供日期以来未进行过修改)。

另外,通常,如果可以的话,发送Content-Length标头。(您正在stat-ing文件,因此您应该拥有此文件。)


@isaacs,能否请您举例说明如何实现这三个步骤,谢谢!
Eugene Kuzmenko 2015年

1
bufferSize选项已不推荐使用highWaterMark
Umair Ishaq'7

3
这如何回答最初提出的问题?
CapturedTree

45

fs.readFile会按照您所指出的那样fs.createReadStream将整个文件加载到内存中,而as则会以您指定大小的块读取文件。

客户端还将使用fs.createReadStream读取时分块发送的数据来开始更快地接收数据,而asfs.readFile则会读取整个文件,然后才开始将其发送到客户端。这可能可以忽略不计,但是如果文件很大而磁盘速度很慢,则可以有所作为。

但是请考虑一下,如果在100MB的文件上运行这两个功能,第一个将使用100MB的内存来加载文件,而后者仅使用最多4KB的内存。

编辑:我真的看不到您为什么要使用任何原因,fs.readFile特别是因为您说过将打开大文件。


这意味着fs.readFile我们无法捕捉每个示例的进度吗?
Elemento0 2015年

4

如果文件很大,则“ 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 !!");
});

实际上,您不会看到“完成!” 信息。由于缓冲区的大小不足以容纳文件内容,因此“ readFile”将无法读取文件内容。

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

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


0

另一个(也许不是那么知名)的事情是,我相信Nodefs.readFile与使用相比在清除使用后的未使用内存方面更好fs.createReadStream。您应该对此进行测试以验证最有效的方法。另外,我知道通过Node的每个新版本,情况都变得更好了(即,在这种情况下,垃圾收集器变得越来越聪明)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.