You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 20:05:42