使用Node.js编写大型文件

使用Node.js编写大型文件,node.js,large-files,Node.js,Large Files,我正在使用node.js编写一个大文件,使用: var fs=require('fs'); var stream=fs.createWriteStream('someFile.txt',{flags:'w'}); var线; while(lines=getLines()){ 对于(变量i=0;i

我正在使用node.js编写一个大文件,使用:

var fs=require('fs');
var stream=fs.createWriteStream('someFile.txt',{flags:'w'});
var线;
while(lines=getLines()){
对于(变量i=0;i

我想知道这个方案不使用事件是否安全?如果不是(我认为是这样),将任意大数据写入文件的模式是什么?

drain背后的想法是,您可以使用它在此处进行测试:

var fs = require('fs');
var stream = fs.createWriteStream('someFile.txt', {flags: 'w'});

var lines;
while (lines = getLines()) {
    for (var i = 0; i < lines.length; i++) {
        stream.write(lines[i]); //<-- the place to test
    }
}
var fs=require('fs');
var stream=fs.createWriteStream('someFile.txt',{flags:'w'});
var线;
while(lines=getLines()){
对于(变量i=0;istream.write(行[i]);//[Edit]更新后的Node.js说:

[The]返回值是严格的建议值。您可以继续写入,即使它返回false。但是,写入操作将在内存中缓冲,因此最好不要过度执行此操作。相反,在写入更多数据之前,请等待drain事件

[原件]来自(重点矿):

如果字符串已刷新到内核缓冲区,则返回
true
。返回
false
以指示内核缓冲区已满,并且将来将发送数据


我将其解释为,如果给定字符串立即写入底层操作系统缓冲区,则“write”函数返回
true
;如果该字符串尚未写入,但将由write函数写入(例如,WriteStream可能为您缓冲),则返回
false
,这样您就不必调用“write”再次说明。

我最终就是这样做的。背后的想法是创建可读的流实现接口,然后使用
pipe()
方法将数据传输到可写流

var fs = require('fs');
var writeStream = fs.createWriteStream('someFile.txt', { flags : 'w' });
var readStream = new MyReadStream();

readStream.pipe(writeStream);
writeStream.on('close', function () {
    console.log('All done!');
});

MyReadStream
类的示例可以从mongoose中获得。

我发现流处理大文件的性能很差-这是因为您无法设置足够的输入缓冲区大小(至少我不知道有什么好的方法)。这就是我所做的:

var fs = require('fs');

var i = fs.openSync('input.txt', 'r');
var o = fs.openSync('output.txt', 'w');

var buf = new Buffer(1024 * 1024), len, prev = '';

while(len = fs.readSync(i, buf, 0, buf.length)) {

    var a = (prev + buf.toString('ascii', 0, len)).split('\n');
    prev = len === buf.length ? '\n' + a.splice(a.length - 1)[0] : '';

    var out = '';
    a.forEach(function(line) {

        if(!line)
            return;

        // do something with your line here

        out += line + '\n';
    });

    var bout = new Buffer(out, 'ascii');
    fs.writeSync(o, bout, 0, bout.length);
}

fs.closeSync(o);
fs.closeSync(i);

处理这一问题的最干净的方法是将您的线路生成器设为a—我们称之为
lineReader
。然后,以下操作将自动处理缓冲区,并为您很好地进行排水:

lineReader.pipe(fs.createWriteStream('someFile.txt'));
如果您不想生成可读的流,您可以监听
write
的输出以获取缓冲区的满度,并做出如下响应:

var i = 0, n = lines.length;
function write () {
  if (i === n) return;  // A callback could go here to know when it's done.
  while (stream.write(lines[i++]) && i < n);
  stream.once('drain', write);
}
write();  // Initial call.
var i=0,n=lines.length;
函数写入(){
if(i==n)return;//回调可以转到这里来知道何时完成。
while(stream.write(行[i++])&&i

关于这种情况,可以找到一个较长的例子。

对于这个问题,有几个建议的答案完全忽略了关于流的要点

本模块可以提供帮助

但是,让我们假设所描述的情况并自己编写代码

如果您试图直接流式传输到文件,这将导致问题-类似于“无效的非字符串/缓冲区块”错误

这类问题的解决方法非常简单

只需在可读和可写之间进行另一个转换,将可读对象适配为可适当写入的字符串

示例代码解决方案:

var fs = require('fs'),
    writeStream = fs.createWriteStream('./out' + process.pid, {flags: 'w', encoding: 'utf-8' }),
    stream = require('stream'),
    stringifier = new stream.Transform();
stringifier._writableState.objectMode = true;
stringifier._transform = function (data, encoding, done) {
    this.push(JSON.stringify(data));
    this.push('\n');
    done();
}
rowFeedDao.getRowFeedsStream(merchantId, jobId)
.pipe(stringifier)
.pipe(writeStream).on('error', function (err) {
   // handle error condition
}

如果您碰巧没有输入流,您就不能轻松地使用管道。 以上这些对我都不起作用,排水事件不会触发。解决方法如下(基于泰勒的回答):

var行[];//一些非常大的数组
var i=0;
函数写入(){
如果(i<线长度){
wstream.write(第[i]行),函数(err){
如果(错误){
控制台日志(err);
}否则{
i++;
write();
}
});
}否则{
wstream.end();
控制台日志(“完成”);
}
};
write();

but“以这种方式写入文件描述符时,在流耗尽之前关闭描述符有发送无效(已关闭)FD的风险。”让我觉得缓冲区已满意味着它不能再接受您的任何代码。我真的不知道,在这里我只是给出了我最好的猜测作为答案。@jcolebrand:是的,我也不知道,但我猜是“排水管”事件只是表示操作系统已准备好立即写入,以防您真的想要避免任何类型的缓冲,无论是您自己的缓冲还是来自WriteStream“write”方法。然而,“drain”的文档提到“可以安全地再次写入”,这要么是一个糟糕的措辞选择,要么是反对我解释的证据!没有必要使用您自己的缓冲区。Node.js已经为您完成了。读取源文件nodejs source/lib/fs.js#WriteStream.prototype.write为什么需要ReadStream()当我们只对将内容写入文件感兴趣时?@nab谢谢。在管道中似乎没有为换行添加
\r\n
,因此将每一行合并为一行…找不到QueryStream readStream接口链接已断开。在
readStream/writeStream
readSync/writeSync
之间是否有任何基准测试以确认此答案?谢谢。定义的“bout”变量是什么?要消耗,可以使用承诺
lineReader.pipe(fs.createWriteStream('someFile.txt'));
var i = 0, n = lines.length;
function write () {
  if (i === n) return;  // A callback could go here to know when it's done.
  while (stream.write(lines[i++]) && i < n);
  stream.once('drain', write);
}
write();  // Initial call.
var fs = require('fs'),
    writeStream = fs.createWriteStream('./out' + process.pid, {flags: 'w', encoding: 'utf-8' }),
    stream = require('stream'),
    stringifier = new stream.Transform();
stringifier._writableState.objectMode = true;
stringifier._transform = function (data, encoding, done) {
    this.push(JSON.stringify(data));
    this.push('\n');
    done();
}
rowFeedDao.getRowFeedsStream(merchantId, jobId)
.pipe(stringifier)
.pipe(writeStream).on('error', function (err) {
   // handle error condition
}
var lines[]; // some very large array
var i = 0;

function write() {
    if (i < lines.length)  {
        wstream.write(lines[i]), function(err){
            if (err) {
                console.log(err);
            } else {
                i++;
                write();
            }
        });
    } else {
        wstream.end();
        console.log("done");
    }
};
write();