本章介绍如何从 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
实例,在底层,框架将负责管道传输响应。
Streamable File 类
StreamableFile
是一个保存要返回的流的类。要创建新的 StreamableFile
,您可以将 Buffer
或 Stream
传递给 StreamableFile
构造函数。
可以从 @nestjs/common
导入 StreamableFile
类。
跨平台支持
默认情况下,Fastify 可以支持发送文件而无需调用 stream.pipe(res)
,因此您根本不需要使用 StreamableFile
类。但是,Nest 支持在两种平台类型中使用StreamableFile
,因此如果您最终在 Express 和 Fastify 之间切换,则无需担心两个引擎之间的兼容性。
示例
您可以在下面找到一个将package.json
作为文件而不是 JSON 返回的简单示例,但这个想法自然可以扩展到图像、文档和任何其他文件类型。
ts
import { createReadStream } from 'node:fs'
import { join } from 'node:path'
import { Controller, Get, StreamableFile } from '@nestjs/common'
@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 { createReadStream } from 'node:fs'
import { join } from 'node:path'
import { Controller, Get, Res, StreamableFile } from '@nestjs/common'
import type { Response } from 'express' // Assuming that we are using the ExpressJS HTTP Adapter
@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"',
// If you want to define the Content-Length value to another value instead of file's length:
// length: 123,
})
}
// Or even:
@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)
}
// Or even:
@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)
}
}