NestJS流式将MySQL查询结果转CSV发送至前端失败求助
问题:MySQL查询结果流式转CSV无法发送至前端
尝试将MySQL查询结果通过Node.js流转换为CSV并发送给客户端,但转换过程正常,文件却无法送达前端。以下是当前代码:
@Get('csv') public async exportCases( @Query('params') params: string, @Request() req: AuthenticatedRequest, @Res() res: Response, @I18n() i18n: I18nContext ) { const result = await this.caseService.streamAllByCompanyId( req.user.currentCompanyId as string, decodeTableParams(params) ); let prependHeader = true; res.header('Content-Type', 'text/csv'); res.attachment('cases.csv'); const transform = new stream.Transform({ objectMode: true, encoding: 'utf-8', }); transform._transform = async (chunk, _, done): Promise<void> => { const casesWithJe: CreateCaseCsvDto[] = this.convertStreamToCaseCsvDto( [chunk], i18n ); const convertedToCsv = await new Promise((resolve) => resolve(this.convertCasesToCsv(casesWithJe, i18n, prependHeader)) ); prependHeader = false; done(null, convertedToCsv); }; const writeFile = fs.createWriteStream('cases.csv'); // const readFile = fs.createReadStream('cases.csv'); result.pipe(transform).pipe(writeFile); }
解决方案
核心问题是当前代码将转换后的CSV流写入了服务器本地文件,而非直接通过响应对象res发送给客户端。按以下步骤修复:
- 移除本地文件写入逻辑:删掉
const writeFile = fs.createWriteStream('cases.csv');,不需要在服务器生成本地文件,直接把流导向客户端响应。 - 将转换后的流导向响应对象:把
result.pipe(transform).pipe(writeFile);改成result.pipe(transform).pipe(res);,这样转换后的CSV内容会直接通过HTTP响应流式发送给前端。 - 优化异步Transform处理(可选但推荐):规范异步逻辑,捕获转换错误避免流异常:
transform._transform = async (chunk, _, done) => { try { const casesWithJe: CreateCaseCsvDto[] = this.convertStreamToCaseCsvDto([chunk], i18n); const convertedToCsv = await this.convertCasesToCsv(casesWithJe, i18n, prependHeader); prependHeader = false; done(null, convertedToCsv); } catch (err) { done(err); // 捕获错误并传递给流处理 } }; - 确保响应头正确设置:用更明确的写法避免框架兼容问题:
res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename="cases.csv"');
修复后的完整代码:
@Get('csv') public async exportCases( @Query('params') params: string, @Request() req: AuthenticatedRequest, @Res() res: Response, @I18n() i18n: I18nContext ) { const result = await this.caseService.streamAllByCompanyId( req.user.currentCompanyId as string, decodeTableParams(params) ); let prependHeader = true; // 设置响应头 res.setHeader('Content-Type', 'text/csv'); res.setHeader('Content-Disposition', 'attachment; filename="cases.csv"'); const transform = new stream.Transform({ objectMode: true, encoding: 'utf-8', }); transform._transform = async (chunk, _, done) => { try { const casesWithJe: CreateCaseCsvDto[] = this.convertStreamToCaseCsvDto([chunk], i18n); const convertedToCsv = await this.convertCasesToCsv(casesWithJe, i18n, prependHeader); prependHeader = false; done(null, convertedToCsv); } catch (err) { done(err); } }; // 将流直接导向客户端响应 result.pipe(transform).pipe(res); }
内容的提问来源于stack exchange,提问作者mohamed.fr
相关产品推荐
相关产品推荐

