What are the pros and cons of fs.createReadStream vs fs.readFile in 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();
});

如果所涉及的文件比较大,比如视频,那么 fs.createReadStream 可能提供更好的用户体验,这种假设是否正确?感觉好像不那么像块状物; 这是真的吗?是否还有其他的优点、缺点、注意事项或陷阱我需要知道?

36996 次浏览

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

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

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

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

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)

read.pipe(write)sys.pump(read, write)方法还具有添加流控制的好处。因此,如果写入流不能很快地接受数据,它会告诉读取流后退,以尽量减少在内存中缓冲的数据量。

flags:"r"mode:0666隐含的事实是,它是一个 FileReadStream。不推荐使用 binary编码——如果没有指定编码,它将只使用原始数据缓冲区。

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

  1. Sniff for req.headers.range and see if it matches a string like /bytes=([0-9]+)-([0-9]+)/. If so, you want to just stream from that start to end location. (Missing number means 0 or "the end".)
  2. 从 stat ()调用散列 inode 和创建时间到 ETag 头。如果您得到一个“ If-none-match”匹配该标头的请求标头,请发回一个 304 Not Modified
  3. 根据 stat 对象上的 mtime日期检查 if-modified-since头。304,如果它没有修改,因为日期提供。

另外,一般来说,如果可以的话,发送一个 Content-Length头文件(您正在对文件进行 stat处理,因此应该有这个头文件)

另一个可能不那么广为人知的事情是,我相信 Node 在使用 fs.readFile后比使用 fs.createReadStream更善于清理未使用的内存。您应该对此进行测试,以验证哪种方法最有效。此外,我知道,每一个新版本的 Node,这已经变得更好(即垃圾收集器已经变得更聪明的这些类型的情况)。

如果它是一个大文件,那么“ readFile”将占用内存,因为它缓冲了内存中所有的文件内容,并可能挂起您的系统。 While ReadStream read in chunks.

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

 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" wouldn't be able to read the file content as buffer is not big enough to hold the file content.

现在不要使用“ readFile”,而是使用 readStream 并监视内存使用情况。

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