首页 > 解决方案 > 10+错误后如何推送到节点流?

问题描述

我最近拿起了一些旧的流代码(在 8.x 是 LTS 时编写的)并尝试将其更新到 12.x。这导致我处理 ENOENT 文件错误的方式发生了有趣的变化。

这是一个简化:

const { createServer } = require('http')
const { createReadStream } = require('fs')

const PORT = 3000

const server = createServer((req, res) => {
  res.writeHead(200, {
    'Content-Type': 'application/json'
  })

  const stream = createReadStream(`not-here.json`, {encoding: 'utf8'})

  stream.on('error', err => {
    stream.push(JSON.stringify({data: [1,2,3,4,5]}))
    stream.push(null)
  })

  stream.pipe(res)
})

server.listen(PORT)
server.on('listening', () => {
  console.log(`Server running at http://localhost:${PORT}/`)
})

在 Node 8 中,上面的代码可以正常工作。我能够拦截错误,向流中写入一些内容并让它正常关闭。

在节点 10+(测试 10、12 和 13)中,当我的错误回调被调用时,流已经被销毁。我无法在流中推送新内容并为客户端优雅地处理错误。

这是一个有意的改变吗,我仍然可以以一种很好的方式为克林特处理这个错误吗?

标签: node.jsnode-streams

解决方案


一种可能。自己打开文件,只使用已成功打开的文件创建流。这将允许您在进入混乱的流错误处理机制之前处理 ENOENT(或打开文件时的任何其他错误)。流架构似乎与错误中止最为一致,而不是通过一些替代行为进行恢复。

const { createServer } = require('http');
const fs = require('fs');

const PORT = 3000;

const server = createServer((req, res) => {
    res.writeHead(200, {'Content-Type': 'application/json'});

    fs.open('not-here.json', {encoding: 'utf8'}, (err, fd) => {
        if (err) {
            // send alternative response here
            res.end(JSON.stringify({data: [1,2,3,4,5]}));
        } else {
            const stream = fs.createReadStream(null, {fd, encoding: 'utf8'});
            stream.pipe(res);
        }
    });
});

server.listen(PORT);
server.on('listening', () => {
    console.log(`Server running at http://localhost:${PORT}/`)
});

您还可以尝试在您的流上使用autoDestroyorautoClose选项,看看是否有任何这些标志将允许流仍然打开以便您将数据推送到其中,即使文件创建了打开或读取错误。这些标志上的文档不是很完整,因此需要结合编程实验和研究代码来查看它们是否可以被操纵以在流出错后仍将数据添加到流中。


推荐阅读