跳到主要内容

Streaming files

info Note This chapter shows how you can stream files from your HTTP application. The examples presented below do not apply to GraphQL or Microservice applications.

There may be times where you would like to send back a file from your REST API to the client. To do this with Nest, normally you'd do the following:

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

But in doing so you end up losing access to your post-controller interceptor logic. To handle this, you can return a StreamableFile instance and under the hood, the framework will take care of piping the response.

Streamable File class

A StreamableFile is a class that holds onto the stream that is to be returned. To create a new StreamableFile, you can pass either a Buffer or a Stream to the StreamableFile constructor.

info hint The StreamableFile class can be imported from @nestjs/common.

Cross-platform support

Fastify, by default, can support sending files without needing to call stream.pipe(res), so you don't need to use the StreamableFile class at all. However, Nest supports the use of StreamableFile in both platform types, so if you end up switching between Express and Fastify there's no need to worry about compatibility between the two engines.

Example

You can find a simple example of returning the package.json as a file instead of a JSON below, but the idea extends out naturally to images, documents, and any other file type.

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);
}
}

The default content type (the value for Content-Type HTTP response header) is application/octet-stream. If you need to customize this value you can use the type option from StreamableFile, or use the res.set method or the @Header() decorator, like this:

import { Controller, Get, StreamableFile, Res } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';
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);
}
}