Skip to content

流式文件

注意

本章展示了如何从你的 HTTP 应用程序中流式传输文件。下面提供的示例不适用于 GraphQL 或微服务应用程序。

有时你可能希望从 REST API 向客户端发送文件。要在 Nest 中做到这一点,通常你会这样做:

ts
@Controller('file')
export class FileController {
  @Get()
  getFile(@Res() res: Response) {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    file.pipe(res);
  }
}

但这样做会导致你失去对控制器后置拦截器逻辑的访问。要处理这个问题,你可以返回一个 StreamableFile 实例,框架会在底层负责管道传输响应。

StreamableFile 类

StreamableFile 是一个持有要返回的流的类。要创建一个新的 StreamableFile,你可以将 BufferStream 传递给 StreamableFile 构造函数。

提示

StreamableFile 类可以从 @nestjs/common 导入。

跨平台支持

Fastify 默认可以支持发送文件而无需调用 stream.pipe(res),因此你根本不需要使用 StreamableFile 类。然而,Nest 在两种平台类型中都支持使用 StreamableFile,因此如果你在 Express 和 Fastify 之间切换,无需担心两个引擎之间的兼容性。

示例

你可以在下面找到一个将 package.json 作为文件而不是 JSON 返回的简单示例,但这个思路自然可以扩展到图片、文档和任何其他文件类型。

ts
import { Controller, Get, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'node:fs';
import { join } from 'node:path';

@Controller('file')
export class FileController {
  @Get()
  getFile(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    return new StreamableFile(file);
  }
}

默认的内容类型(Content-Type HTTP 响应头的值)是 application/octet-stream。如果你需要自定义此值,可以使用 StreamableFiletype 选项,或使用 res.set 方法或 @Header() 装饰器,如下所示:

ts
import { Controller, Get, StreamableFile, Res } from '@nestjs/common';
import { createReadStream } from 'node:fs';
import { join } from 'node:path';
import type { Response } from 'express'; // 假设我们使用 ExpressJS HTTP 适配器

@Controller('file')
export class FileController {
  @Get()
  getFile(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    return new StreamableFile(file, {
      type: 'application/json',
      disposition: 'attachment; filename="package.json"',
      // 如果你想将 Content-Length 值定义为文件长度以外的其他值:
      // length: 123,
    });
  }

  // 或者:
  @Get()
  getFileChangingResponseObjDirectly(@Res({ passthrough: true }) res: Response): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    res.set({
      'Content-Type': 'application/json',
      'Content-Disposition': 'attachment; filename="package.json"',
    });
    return new StreamableFile(file);
  }

  // 或者:
  @Get()
  @Header('Content-Type', 'application/json')
  @Header('Content-Disposition', 'attachment; filename="package.json"')
  getFileUsingStaticValues(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    return new StreamableFile(file);
  }
}

基于 NestJS 官方文档翻译