nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
67.07k stars 7.56k forks source link

Example of app Nest + Angular + Multer #262

Closed 32penkin closed 6 years ago

32penkin commented 6 years ago

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.

jamakase commented 6 years ago

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.

32penkin commented 6 years ago

@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.

pterblgh commented 6 years ago

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.

pterblgh commented 6 years ago

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

kamilmysliwiec commented 6 years ago

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.

alexfrad commented 6 years ago

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

luanxuechao commented 5 years ago

@pterblgh how to fix , I have problem too.

pterblgh commented 5 years ago

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

luanxuechao commented 5 years ago

@pterblgh thank you, I fix it.

lock[bot] commented 5 years ago

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.