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

Posted

技术标签:

【中文标题】node.js 中 fs.createReadStream 与 fs.readFile 的优缺点是啥?【英文标题】:What are the pros and cons of fs.createReadStream vs fs.readFile in node.js?node.js 中 fs.createReadStream 与 fs.readFile 的优缺点是什么? 【发布时间】:2011-06-03 03:34:01 【问题描述】:

我正在研究 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 可能会提供更好的用户体验是否正确?感觉它可能不那么块状了;这是真的?我还需要了解其他优点、缺点、注意事项或陷阱吗?

【问题讨论】:

【参考方案1】:

如果您只是要将“数据”连接到“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 暗示它是 FileReadStreambinary 编码已弃用——如果未指定编码,它将仅适用于原始数据缓冲区。

此外,您还可以添加一些其他好东西,让您的文件更加流畅:

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

另外,一般来说,如果可以,请发送Content-Length 标头。 (你是 stat-ing 文件,所以你应该有这个。)

【讨论】:

@isaacs,您能否举例说明如何实施这 3 个步骤,谢谢! bufferSize 选项已被弃用,取而代之的是 highWaterMark 这如何回答最初提出的问题?【参考方案2】:

fs.readFile 将按照您的指示将整个文件加载到内存中,而 fs.createReadStream 将按照您指定的大小块读取文件。

客户端也将开始使用fs.createReadStream 更快地接收数据,因为它在读取时以块的形式发送出去,而fs.readFile 将读取整个文件,然后才开始将其发送给客户端。这可能可以忽略不计,但如果文件很大并且磁盘很慢,则可能会有所不同。

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

编辑:我真的不明白你为什么要使用fs.readFile,尤其是因为你说你将打开大文件。

【讨论】:

这意味着使用fs.readFile 我们无法了解每个示例的进度?【参考方案3】:

如果它是一个大文件,那么“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”将无法读取文件内容,因为缓冲区不足以容纳文件内容。

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

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

【讨论】:

【参考方案4】:

另一件也许不太为人所知的事情是,我相信在使用fs.readFile 之后,与fs.createReadStream 相比,Node 更擅长清理未使用的内存。您应该对此进行测试以验证最有效的方法。另外,我知道每一个新版本的 Node,这都会变得更好(即垃圾收集器在这些类型的情况下变得更聪明)。

【讨论】:

以上是关于node.js 中 fs.createReadStream 与 fs.readFile 的优缺点是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 node.js 中找到前 10 个最慢的函数?分析 node.js

Node.js:如何将 Node.js 嵌入 HTML?

Node.js 模块

[Node.js]Buffer

PHP 与 Node.js - 在 Node.js 中使用 Jade 的 HTML 渲染速度会变慢吗?

node.js 项目中的 index.js 用于啥?