流式文件
注意
本章展示了如何从你的 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,你可以将 Buffer 或 Stream 传递给 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。如果你需要自定义此值,可以使用 StreamableFile 的 type 选项,或使用 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);
}
}