Techniques
流式文件
学习如何在 NestJS HTTP 应用中流式传输文件,包括 StreamableFile 类的使用和跨平台支持
流式文件
注意 本章展示了如何从您的 HTTP 应用程序流式传输文件。下面提供的示例不适用于 GraphQL 或微服务应用程序。
有时您可能希望从 REST API 向客户端发送文件。要在 Nest 中执行此操作,通常您会执行以下操作:
@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 返回,但这个想法自然地扩展到图像、文档和任何其他文件类型。
import { Controller, Get, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from '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() 装饰器,如下所示:
import { Controller, Get, StreamableFile, Res } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from '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);
}
}