Skip to content

Instantly share code, notes, and snippets.

@jonilsonds9
Last active October 17, 2023 23:11
Show Gist options
  • Save jonilsonds9/efc228e34a298fa461d378f48ef67836 to your computer and use it in GitHub Desktop.
Save jonilsonds9/efc228e34a298fa461d378f48ef67836 to your computer and use it in GitHub Desktop.
Uploading binary file (buffer) using NestJS
// Had to upload a binary file but multer only works with `multipart/form-data`
// And NestJS parses the request. So I had to manually upload it.
// First in `main.ts` I had to edit the following line:
const app = await NestFactory.create(AppModule);
// For:
const app = await NestFactory.create(AppModule, { bodyParser: false });
// So NestJS allows us to handle the request.
// The next step is to create a Middleware, I named it `FileBufferMiddleware`
// I added the following content:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { raw } from 'body-parser';
@Injectable()
export class FileBufferMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void): any {
raw({
verify: (req, res, buffer) => {
req['fileBuffer'] = buffer;
},
limit: '5mb',
})(req, res as any, next);
}
}
// We need to add to our `MiddlewareModule` like so:
export class MiddlewareModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(FileBufferMiddleware)
.forRoutes({
path: '/upload/:fileName',
method: RequestMethod.PUT,
});
}
}
// With that we will have a `fileBuffer` which is a Buffer object in the request.
// And to make it even easier, I also created a decorator named `FileBuffer`:
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const FileBuffer = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.fileBuffer || null;
},
);
// So, we can just use the decorator in the Controller and we will already have the object ready to manipulate:
@Put('/upload/:fileName')
upload(
@Res() response: Response,
@Param() fileName: string,
@FileBuffer() fileBuffer,
): object {
if (fileBuffer === null) throw new BadRequestException('File is required');
const writeStream = createWriteStream(`./data/${fileName}`);
writeStream.write(fileBuffer);
return response
.status(HttpStatus.CREATED)
.send({ HttpCode: 201, Message: 'File uploaded.' });
}
// And that's it, now we have an upload using a binary file.
@emiliosanches
Copy link

Thank you! Very useful.
But, looking to the code, I think it would disable the automatic request body parsing for all requests.
Is it possible to implement this behavior only for one route and keep body-parser working normally for every other route?

@jonilsonds9
Copy link
Author

Hi @emiliosanches, well I didn't quite understand the behavior you want, because the way I did it in gist, it only works for the upload route, the other routes Middleware doesn't work. Could you explain better?

@MastalerzKamil
Copy link

@jonilsonds9 my quesstion is how to test it manually? Is there any way e.g. in Postman to test uploading file by using proposed solution?

@jonilsonds9
Copy link
Author

@MastalerzKamil Yes, there's a way to test it, it's very simple, in Postman you must send a request of type Put to the endpoint /upload/filename.png, and then in the Body of the request mark the type as binary and then use the Select File button and select the file you want to upload to the API, see the image:

binary file postman

Using Insomnia is even simpler, you should send a Put request to the /upload/filename.png endpoint, then in the Body of the request select Binary File and in the field SELECTED FILE click and select the file you want to upload and then test, see the image:

binary file insomnia

@UncleFirefox
Copy link

Just in case somebody wants to keep the old behavior for some routes, this a really cool article that walks you through: https://www.darraghoriordan.com/2021/06/20/parse-request-raw-body-nest-js-controller/

@MegaSpaceHamlet
Copy link

Thanks for this.

One thing to note: for me I kept on getting the BadRequestException until I added the following line to the raw functions options:

type: image/jpeg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment