【问题标题】:Node.js: How to write() on Write Stream 'finish' eventNode.js:如何在写入流“完成”事件上写入()
【发布时间】:2023-04-05 22:22:01
【问题描述】:

我正在使用 Node.js 流逐行浏览文本文件,进行一些转换并输出到 SVG 文件。

我正在尝试在处理完成后写入最后一条数据(</svg>),但是当写入流发出finish 事件时,尝试write() 将抛出Error: write after end

有什么优雅的方法可以解决这个问题吗?

注意:输入文件很大(大约 1GB),因此由于其 I/O 和内存管理,无法绕过 pipe() 方法。

var fs = require('fs');
var split2 = require('split2');
var through2 = require('through2');

var read_stream = fs.createReadStream('input.txt');
var write_stream = fs.createWriteStream('output.svg');

write_stream.write('<svg>');
write_stream.on('finish', function() {
  this.write('</svg>'); // doesn't work
});

read_stream
  .pipe(split2())
  .pipe(through2.obj(function(line, encoding, next) {
     this.push(line);
     next();
  }))
  .pipe(write_stream);

解决方案

感谢 JordanpNre 帮助我解决这个问题。

解决方案 1(通用)

pipe() 使用end:false 选项写入流并手动end() 流。

var fs = require('fs');
var split2 = require('split2');
var through2 = require('through2');

var read_stream = fs.createReadStream('input.txt');
var write_stream = fs.createWriteStream('output.svg');

write_stream.write('<svg>');

read_stream
  .pipe(split2())
  .pipe(through2.obj(function(line, encoding, next) {
     this.push(line);
     next();
  }))
  .pipe(write_stream, { end: false });

read_stream.on('end', function() {
  write_stream.end('</svg>');
});

解决方案 2(特定于 through/through2 转换流)

through2有一个flush函数,可以用来写入最终数据。

var fs = require('fs');
var split2 = require('split2');
var through2 = require('through2');

var read_stream = fs.createReadStream('input.txt');
var write_stream = fs.createWriteStream('output.svg');

write_stream.write('<svg>');

read_stream
  .pipe(split2())
  .pipe(through2.obj(function(line, encoding, next) {
    this.push(line);
    next();
  }, function(flush) {
    this.push('</svg>');
    flush();
  }))
  .pipe(write_stream);

【问题讨论】:

  • 您好!我建议将您的解决方案从问题提取到答案部分。
  • @SlavaFominII 你能给我一些指导吗?
  • @DanBurzo 我相信 SlavaFominII 建议您编辑您的问题并删除您附加的解决方案。然后,您可以创建自己的问题答案,或通过 cmets 建议对 Jordan 和/或 pNre 的答案进行编辑。

标签: javascript node.js stream


【解决方案1】:

似乎管道在完成后关闭了流。

http://nodejs.org/api/stream.html 的文档指出:

默认情况下,当源流发出 end 时,会在目标上调用 end(),因此目标不再可写。传递 { end: false } 作为保持目标流打开的选项。

这会使 writer 保持打开状态,以便在最后写入“Goodbye”。

reader.pipe(writer, { end: false });
reader.on('end', function() {
  writer.end('Goodbye\n');
});

【讨论】:

  • 感谢您抽出宝贵时间查看问题。我添加了一个澄清说明:文件非常大,所以我不能放弃pipe(),以免内存不足。
  • @Dan,我在网站上找到了您要的内容,我会更新我的答案
  • 啊,我完全错过了文档中的内容!谢谢,正是我需要的。
【解决方案2】:

似乎有一个名为'prefinish' 的未记录事件。 不过我没用过。

【讨论】:

    【解决方案3】:

    您是否考虑过创建一个新流来附加&lt;/svg&gt; 标记? through 可以帮助您:

    var fs = require('fs');
    var split2 = require('split2');
    var through = require('through');
    
    var read_stream = fs.createReadStream('input.txt');
    var write_stream = fs.createWriteStream('output.svg');
    
    write_stream.write('<svg>');
    var tag = through(function write(data) {
        this.queue(data);
    }, function end() {
        this.queue('</svg>');
    });
    
    read_stream.pipe(split2()).pipe(some_transform).pipe(tag).pipe(write_stream);
    

    【讨论】:

    • 啊,当然!我使用through2 进行转换,所以我可以添加一个刷新函数。我知道这是可用的,但不知何故,当我在寻找解决方案时,它并没有在我的意识中注册:-) 谢谢!
    【解决方案4】:

    最近遇到了这个问题,找到了一个更优雅的解决方案。在原生 Transform 流上有一个(很好的)记录在案的 _flush 方法。

    https://nodejs.org/api/stream.html#stream_transform_flush_callback

    解决方案如下所示:

    const fs = require('fs')
    const split2 = require('split2')
    const { Transform } = require('stream')
    
    const input = fs.createReadStream('input.txt')
    const output = fs.createWriteStream('output.svg')
    
    class SVGWrapper extends Transform {
        constructor(){ this.push('<svg>') }
    
        _flush(done){ this.push('</svg>') }
    
        _transform(line, enc, next){
            this.push(line)
            next()
        }
    }
    
    input
        .pipe(split2())
        .pipe(new SVGWrapper)
        .pipe(output)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-28
      • 1970-01-01
      • 2013-03-22
      • 2016-05-13
      • 2014-11-05
      • 1970-01-01
      • 2023-03-15
      • 2021-09-15
      相关资源
      最近更新 更多