在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);
})