在Koa中如何将DB查询返回的对象流转换为字符串流?
最佳实现方式:用Transform流做对象到JSON数组的流式转换
这问题我之前在项目里碰到过,核心矛盾就是数据库返回的对象模式流(Row实例)没法直接给Koa用——Koa的ctx.body只认字符串/Buffer模式的流,直接传对象流会触发报错。而我们要的是把Row实例逐个转成JSON,同时拼成一个合法的JSON数组流式输出给客户端,不用等所有数据查完再响应。
原生Node.js实现(推荐,无额外依赖)
最稳妥的方式是用Node.js核心的stream.Transform来做中间转换,它能帮我们把对象模式的输入流转换成Koa能处理的字符串流,同时维护JSON数组的格式完整性。
下面是封装好的转换流函数:
const { Transform } = require('stream'); function rowsToJsonArrayStream() { let isFirstRow = true; return new Transform({ writableObjectMode: true, // 允许接收对象类型的chunk(Row实例) readableObjectMode: false, // 输出字符串/Buffer类型的chunk // 处理每个Row实例 transform(row, _, callback) { const rowJson = JSON.stringify(row); if (isFirstRow) { // 第一个元素,先输出数组开头 this.push(`[${rowJson}`); isFirstRow = false; } else { // 后续元素,前面加逗号分隔 this.push(`,${rowJson}`); } callback(); }, // 流结束时收尾,闭合JSON数组 flush(callback) { // 处理空结果的情况,直接输出空数组 this.push(isFirstRow ? '[]' : ']'); callback(); } }); }
然后在Koa路由里这么用:
app.use(async (ctx) => { // 假设这是你的数据库查询流(对象模式) const dbQueryStream = await yourDbDriver.query('SELECT * FROM your_table').stream(); // 一定要设置响应类型为JSON ctx.type = 'application/json'; // 把数据库流通过转换流pipe给ctx.body ctx.body = dbQueryStream.pipe(rowsToJsonArrayStream()); // 别忘了处理数据库流的错误,避免服务挂掉 dbQueryStream.on('error', (err) => { ctx.status = 500; ctx.body = { error: '数据库查询失败', details: err.message }; }); });
第三方库简化实现(快速开发)
如果不想自己写转换逻辑,也可以用现成的npm包,比如json-stream-stringify,它专门做这件事:
const JsonStreamStringify = require('json-stream-stringify'); app.use(async (ctx) => { const dbQueryStream = await yourDbDriver.query('SELECT * FROM your_table').stream(); ctx.type = 'application/json'; ctx.body = dbQueryStream.pipe(new JsonStreamStringify()); // 同样要处理错误 dbQueryStream.on('error', (err) => { ctx.status = 500; ctx.body = { error: err.message }; }); });
关键注意点
- 响应类型必须设置:一定要加
ctx.type = 'application/json',否则客户端可能无法正确解析返回的JSON数组。 - 错误处理不能少:数据库流可能会抛出错误(比如连接中断),必须监听
error事件,及时返回错误响应,否则会导致Koa进程崩溃。 - 空结果兼容:原生实现里的
flush方法处理了没有数据的情况,确保返回的是合法的[]而不只是[。
内容的提问来源于stack exchange,提问作者asliwinski
相关产品推荐
相关产品推荐

