Nest: Example of app Nest + Angular + Multer

Created on 22 Nov 2017  路  10Comments  路  Source: nestjs/nest

Hi everyone!
I use NesJSt in my project. Now I'm faced with the problem of uploading the file from the client. I already saw the existing Issue # 66, but that did not help me. It is difficult to formulate an issue, but maybe someone has a working example of NestJS + Multer + Angular2. In advance thanks for the answer.

Most helpful comment

Hi,
Since v4.6.0 Nest comes with a basic multer integration. Both @UploadedFile() and FileInterceptor are available from inside @nestjs/common. They can be used in following way:

@Post()
@UseInterceptors(FileInterceptor('filename'))
async upload(@UploadedFile() file) {
    console.log(file)
}

To upload multiple files, you can use @UploadedFiles() and FilesInterceptor. The interceptors accept options object as an argument. The options schema is equal to multer options schema.

All 10 comments

What problem exactly do you have?

F.e. this is how we upload it in our app

Frontend

  public async uploadFiles(payload: UploadFilesDto): Promise<boolean> {
    const formData = new FormData();
    formData.append("document", payload.file);
    return await request(
      `${API_URL}/document`,
      {
        method: "POST",
        body: formData,
      },
    );
  }

Backend
Controller

  @Post("document")
  @HttpCode(HttpStatus.OK)
public async addDocument( @Request() req): Promise<any> {
    const doc: Express.Multer.File = req.files.document[0];
....
  }

Module

export class UserModule implements NestModule {
  public configure(consumer: MiddlewaresConsumer) {
    consumer
      .apply(MulterMiddleware)
      .with({
        dest: "uploads",
      } as multer.Options, [{
        name: "document",
        maxCount: 1,
      }] as multer.Field[])
      .forRoutes({
        path: "v1/document",
        method: RequestMethod.POST,
      });
  }
}

Hope it helps you.

@Jamakase Thanks for the answer! I was not very helped by your answer, could you please attach pieces of imports to your code as well as UploadFilesDto class?
I've tried to do this by the approx similar way. Here it is my code, maybe someone will find any mistake :)
Client side:

import { Component, OnInit } from '@angular/core';
import { CourseRepository } from '../../../@core/repositories/course/course.repository';

@Component({
  selector: 'admin-learning',
  template: `
    <input type="file" (change)="fileChangeEvent($event)" placeholder="Upload file..."/>
    <button type="button" (click)="upload()">Upload</button>
  `,
})
export class AdminLearningComponent implements OnInit {

  filesToUpload: Array<File> = [];

  constructor(private courseRep: CourseRepository) {
  }

  ngOnInit() {
  }

  async upload() {
    const formData: any = new FormData();
    const files: Array<File> = this.filesToUpload;

    formData.append('uploads', files);
    const res = await this.courseRep.upload(formData);
  }

  fileChangeEvent(fileInput: any) {
    this.filesToUpload = <Array<File>>fileInput.target.files;
  }
}

Service:

async upload(file) {
    return this.http.post(`${this.endpoint}/upload`, {data: file})
      .map(res => res.json())
      .map(json => json.data)
      .toPromise();
  }

And the server pieces:

Middleware:

import { Middleware } from '@nestjs/common';
import { NestMiddleware } from '@nestjs/common/interfaces/middlewares';
const multer = require('multer');

@Middleware()
export class FileUploadMiddleware implements NestMiddleware {
  resolve(): (req, res, next) => void {
    const upload = multer({ dest: './uploads/' });
    return upload.any();
  }
}

Module:

import { Module, RequestMethod } from '@nestjs/common';
import { DataBaseModule } from '../db/db.module';
import { LoggerModule } from '../logger';
import { CourseController } from './course.controller';
import { CourseService } from './course.service';
import { MiddlewaresConsumer } from '@nestjs/common/interfaces/middlewares';
import { FileUploadMiddleware } from '../middleware/file-upload.middleware';

@Module({
  controllers: [ CourseController ],
  components: [ CourseService ],
  modules: [
    LoggerModule,
    DataBaseModule,
  ],
})
export class CourseModule {
  configure(consumer: MiddlewaresConsumer) {
    consumer.apply([
      FileUploadMiddleware,
    ]).forRoutes({
      method: RequestMethod.POST,
      path: '/course/upload',
    })
  }
}

Controller:

 @Post('/upload')
  async testUpload(@Response() res, @Request() req, @Body('data') data) {
    console.log(req.files);
    console.log(data);
    res.status(HttpStatus.OK).json({data: 'success'});
  }

I'm not sure, maybe I've done something wrong on my client side? In advance thanks for the answer.

Hi @32penkin!

Did you manage to find a solution integrating Multer with Nestjs properly?

I have the same code from the referred #66 issue (that @kamilmysliwiec wrote), but the req.file and req.files properties are undefined all the time. I send a valid multipart/form-data request to the server using Postman but I can't make it to a processed file.

multer.middleware.ts

import { Middleware, NestMiddleware, ExpressMiddleware } from '@nestjs/common';
import * as multer from 'multer';

@Middleware()
export class MulterMiddleware implements NestMiddleware {
  resolve(...args: any[]): ExpressMiddleware {
    const upload = multer({ dest: './uploads/' });
    /** Accept only one file, using the csv fieldname */
    return upload.single('csv');
  }
}

A module's configure method:

configure(consumer: MiddlewaresConsumer) {
    consumer.apply(MulterMiddleware).forRoutes({
      path: '/admin/campaign/:id/csv',
      method: RequestMethod.PUT,
    });
  }

One of the method belonging to a controller which is imported by the module declaring the upper configure method:

/** All controller methods are prefixed with admin/campaign */
@Put(':id/csv')
async handleUploadedCSV(@Req() req: Request, @Res() res: Response) {
    console.log(`req.files`, req.files);
    console.log(`req.file`, req.file);
    /** Send 200 OK for now */
    return res.sendStatus(HttpStatus.OK);
  }

This is the screenshot of the actual Postman request I'm submitting to the server:
image
There are no additional headers attached to the request.

Sorry for the long comment, but I really don't know what's missing.

Hmm...confusing. I've reloaded the test.csv file in Postman and everything started working without any code modifications. Strange.

Hi,
Since v4.6.0 Nest comes with a basic multer integration. Both @UploadedFile() and FileInterceptor are available from inside @nestjs/common. They can be used in following way:

@Post()
@UseInterceptors(FileInterceptor('filename'))
async upload(@UploadedFile() file) {
    console.log(file)
}

To upload multiple files, you can use @UploadedFiles() and FilesInterceptor. The interceptors accept options object as an argument. The options schema is equal to multer options schema.

Is it planned to add support for the any() function ?

@pterblgh how to fix , I have problem too.

@luanxuechao You should be good to go with @kamilmysliwiec's recommendation: https://github.com/nestjs/nest/issues/262#issuecomment-366098589

@pterblgh thank you, I fix it.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marshall007 picture marshall007  路  3Comments

JulianBiermann picture JulianBiermann  路  3Comments

anyx picture anyx  路  3Comments

cdiaz picture cdiaz  路  3Comments

rlesniak picture rlesniak  路  3Comments