Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of app Nest + Angular + Multer #262

Closed
32penkin opened this issue Nov 22, 2017 · 10 comments
Closed

Example of app Nest + Angular + Multer #262

32penkin opened this issue Nov 22, 2017 · 10 comments

Comments

@32penkin
Copy link

32penkin commented Nov 22, 2017

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
Copy link

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
Copy link
Author

@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
Copy link

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
Copy link

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

@kamilmysliwiec
Copy link
Member

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
Copy link

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

@luanxuechao
Copy link

@pterblgh how to fix , I have problem too.

@pterblgh
Copy link

pterblgh commented Nov 5, 2018

@luanxuechao You should be good to go with @kamilmysliwiec's recommendation: #262 (comment)

@luanxuechao
Copy link

@pterblgh thank you, I fix it.

@lock
Copy link

lock bot commented Sep 24, 2019

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.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants