首页 > 解决方案 > 管道失败后无法发送数据响应

问题描述

我正在尝试从 mysql 查询一些数据,将其转换为 csv,然后最终将其下载为 csv。目标是使用流来实现这一点。

    res.set('Content-Type', 'text/csv')
    res.set('Content-Disposition', `attachment; filename="${req.query?.filename || 'export'}.csv"`)
    const sqlStream = connection.query('SELECT * FROM whatever_table').stream({highWaterMark: 5}) // --> using mysql2 adapter
    const formatCsvStream = formatCsv({delimiter: ';'}) // --> using @fast-csv/format
    try {
        // using {pipeline} from require('stream/promises')
        await pipeline(
            sqlStream,
            formatCsvStream,
            res // --> express response
        )
    } catch (err) {
        console.error(err)
        return res.status(500).json({message: 'Please check the application logs for more details.'}).end()
    }

如果在响应之前没有错误,一切都会按预期工作。但是如果确实发生了错误(例如,我们从表中选择了数据库中不存在的数据),浏览器将使用 ERR_EMPTY_RESPONSE 进行响应,而不是发送在 catch 块中定义的 json 错误消息。

如果我从管道中删除res,浏览器将显示正确的响应(显然在这种情况下下载将不起作用)。

我尝试在返回 .json 之前删除 Content-Type 和 Content-Disposition 标头。我还尝试了常规的 .pipe() 语法,但没有任何运气。

管道失败后如何继续使用快速响应对象?任何建议都会很棒!

标签: node.jsexpress

解决方案


到达catch块时,对象的套接字res已经被管道销毁。如果源中或中间转换发生错误,管道会“转发错误”并破坏目标。

尝试以下操作:

sqlStream.on("error", function(err) {
  console.error(err);
  if (!res.headersSent)
    res.status(500).json({message: 'Please check the application logs for more details.'}).end();
});
try {
  await pipeline(sqlStream, formatCsvStream, res);
} catch (err) {} // to avoid unhandled promise rejections. No further output here.

推荐阅读