在Node.js中如何实现异步文件处理?
本文演示如何使用Node.js和原生Node模块在处理文件等I/O操作中的最佳实践。主要涉及Promise等异步操作语法,文件处理分读操作和写操作两个部分:
读操作
读操作分为读取小文件和大文件两种办法:
首先读取大部分小文件的操作:
- 所有文件读操作都是读取到内存中
- 最大文件大小不能超过2g,任何超过此大小会抛出错误:
is greater than possible Buffer: 0x7fffffff bytes at FSReqWrap.readFileAfterStat [as oncomplete] (fs.js:386:11) - 代码如下:
// imports at top var fs = require('fs');
// Modern approach
function readFile(fileName, options) { return new Promise(function(resolve, reject) { var cb = function cb(err, data) { if (err) { return reject(err); } resolve(data);
};
fs.readFile.call(fs, fileName, options, cb); });
}
// example: readFile(__dirname + "/file.txt").then(function(data) { // data = the whole file console.log(data.length); }).catch(function(err) { console.log(err); })
其次,如果是读取更大的文件:
-
使用下述方法可以读取任何大小的文件,因为它能实时变成字节流。
-
我们设置highWaterMark 为我们希望设置的Buffer大小,一般是512k大小,对于内存存储比较有效。
- 回调会有大量一次性512Kb大小的数据。增加更多字节不会对性能有任何帮助。
- 下面案例演示在三分钟内(212秒)读取7gb大小文件(https://dumps.wikimedia.org/other/wikibase/wikidatawiki/ - latest-all.json.gz)
- 注意: 上述案例的数据是被压缩gzipped ,不能被转为可读格式,为了能解压和流化可读数据可参考下面代码
// imports at top var fs = require('fs');
// Modern approach
function readFileStream(fileName, cb, options) { return new Promise(function(resolve, reject) { var readable = fs.createReadStream(fileName, options); readable.on('data', function(data) { cb(data); })
readable.on('end', function() { resolve(); })
readable.on('error', function(err) { return reject(err); }) }) }
// example: function printDataAsItComesIn(data) { // Data will be null when it reaches the end. if (!data) return;
// type of data = Buffer var stringData = data.toString('utf8', 0, data.length); console.log(stringData.length);
}
readFileStream(__dirname + "/latest-all.json.gz", printDataAsItComesIn, {highWaterMark: 512 * 1024}).then(function() { console.log("\nDone reading"); }).catch(function(err) { console.log(err); })
- 读取和解压大型文件(7gb file fize)
// imports at top var fs = require('fs'); var zlib = require('zlib');
function unzipFileStream(fileName, cb, options) { return new Promise(function(resolve, reject) { var readable = fs.createReadStream(fileName, options); var unzip = zlib.createGunzip({chunkSize: 512 * 1024}) readable.pipe(unzip) unzip.on('data', function(data) { cb(data); })
unzip.on('end', function() { resolve(); })
unzip.on('error', function(err) { return reject(err); }) }) }
// callback that lets us transform the data as it comes in function printDataAsItComesIn(data) { if (!data) return; // data = Buffer // here we can actually read the data in the zip file var stringData = data.toString('utf8', 0, data.length); console.log(stringData); }
// example: unzipFileStream(__dirname + "/latest-all.json.gzip", printDataAsItComesIn, {highWaterMark: 512 * 1024}).then(function() { console.log("\nDone reading"); }).catch(function(err) { console.log(err); })