[NestJS] - 25. 파일 업로드

2023. 3. 6. 23:14
반응형

 

API를 만들다 보면 파일과 같은 바이너리 데이터의 처리를 구현해야 할 수도 있다.

이번에는 NestJS에서 요청으로 받은 파일을 서버 내부에 업로드 하는 과정에 대해서 작성해보려고 한다.

 


패키지 설치

 

yarn add @types/multer --dev

 

Express에서는 파일 업로드 관련 모듈을 제공해주는 Multer 패키지를 통해서 파일 업로드를 구현하고는 한다.

Multer는 요청과 핸들러 사이에서 파일을 처리해주는 미들웨어이다. 

NestJS에서도 기본적으로 Multer를 지원한다. 우리는 타입스크립트로 사용하기 위해서 DevDependency로 다음 모듈을 설치해주자.

 


Request에서 파일 받기

 

 

▶ file.controller.ts

@Controller('file')
export class FileController {
  constructor(private readonly fileService: FileService) {}
  
  @UseInterceptors(FileInterceptor('file'))
  @Post('upload/single')
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    console.log(file)
  }

}
  • Express와 기본적인 작동 방식은 같다. 
  • NestJS에서는 FileInterceptor('Request Body의 필드명') 인터셉터가 요청에서 해당하는 필드의 파일을 선택한다.
  • 해당 인터셉터의 사용을 위해 @UseInterceptors 데코레이터를 사용한다.
  • 인터셉터는 간단하게 말해서 요청 핸들러가 클라이언트로부터 전달받은 인수를 선택해서 파악할 수 있도록 만들어주는 NestJS에 내장된 기능이다.
  • 인터셉터로 받은 파일을 @UploadedFile 데코레이터를 통해서 Multer의 옵션에 따라서 파일을 읽는다. 읽은 결과는 file에 저장된다.

 

 

콘솔에 찍어보면 파일을 잘 읽어들인 것을 확인할 수 있다.

{
  fieldname: 'file',
  originalname: 'google.png',
  encoding: '7bit',
  mimetype: 'image/png',
  buffer: <Buffer 89 50 4e 47 0d ... more bytes>,
  size: 17458
}

 

 

 

Request에서 파일 배열 받기

  @UseInterceptors(FileInterceptor('files'))
  @Post('upload/array')
  uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>) {
    console.log(files);
  }
  • 하나의 필드에서 여러개의 파일을 받아올 때에는 @UploadedFiles를 사용한다.

 

 

Request에서 파일 여러개 받기

  @Post('upload/multi')
  @UseInterceptors(
    FileFieldsInterceptor([
      {
        name: 'thumbnail',
        maxCount: 1,
      },
      {
        name: 'background',
        maxCount: 1,
      },
    ]),
  )
  uploadMultiFiles(
    @UploadedFiles()
    files: {
      thumbnail?: Express.Multer.File;
      background?: Express.Multer.File;
    },
  ) {
    console.log(files);
  }
}
  • 여러개의 필드의 파일을 받을 때에는 인터셉터의 종류를 FileFieldsInterceptor를 사용하고, 배열 안에 필드와 최대 파일 개수를 명시해준다.
  • @UploadedFiles에서 필드별 읽을 파일들을 Object 형식으로 작성해준다.

 

 


파일을 서버에 저장하기

 

이제 파일을 Multer를 통해서 읽을 때 서버 내에 저장해보도록 하겠다.

 

 

▶ /src/utils/upload/multer.config.ts

import { Injectable } from '@nestjs/common';
import * as multer from 'multer';
import * as path from 'path';
import * as fs from 'fs';
import { MulterOptionsFactory } from '@nestjs/platform-express';

@Injectable()
export class MulterConfigService implements MulterOptionsFactory {
  dirPath: string;
  constructor() {
    this.dirPath = path.join(process.cwd(), 'uploads');
    this.mkdir();
  }

  // uploads 폴더 생성
  mkdir() {
    try {
      fs.readdirSync(this.dirPath);
    } catch (err) {
      fs.mkdirSync(this.dirPath);
    }
  }

  createMulterOptions() {
    const dirPath = this.dirPath;
    const option = {
      storage: multer.diskStorage({
        destination(req, file, done) {
          //파일 저장 경로 설정
          done(null, dirPath);
        },

        filename(req, file, done) {
          // 파일명 설정
          const ext = path.extname(file.originalname);
          const name = path.basename(file.originalname, ext);
          done(null, `${name}_${Date.now()}${ext}`); //파일이름_날짜.확장자
        },
      }),
      limits: { fileSize: 10 * 1024 * 1024 }, // 용량 제한
    };
    return option;
  }
}
  • Multer의 옵션을 생성하는 MulterOptionFactory 인터페이스를 서비스 클래스로 구현하였다.
  • 생성자에서는 uploads 폴더가 없을 시 생성한다.
  • createMulterOptions는 MulterOptionFactory에서 구현해야 하는 메서드이다.
    • storage 옵션에 저장할 파일의 경로와 이름 등의 설정을 해준다.
    • destination은 파일 저장 경로를 설정해준다. done()을 통해서 다음 미들웨어로 넘겨주면 된다.
    • filename은 파일 이름을 설정해준다. done()을 통해서 다음 미들웨어로 넘긴다.
    • limits 옵션에서 파일의 용량 제한을 할 수 있다.

 

 

▶ file.module.ts

import { Module } from '@nestjs/common';
import { MulterModule } from '@nestjs/platform-express';
import { MulterConfigService } from 'src/utils/upload/multer.config';
import { FileController } from './file.controller';
import { FileService } from './file.service';

@Module({
  imports: [
    MulterModule.registerAsync({
      useClass: MulterConfigService,
    }),
  ],
  controllers: [FileController],
  providers: [FileService],
})
export class FileModule {}
  • 이제 해당 옵션을 적용하기 위해서 모듈에서 registerAsync를 통해서 만든 서비스를 등록시킨 MulterModule를 imports한다.
  • 이제부터 Multer를 통과하는 파일들은 디렉터리에 저장될 것이다.

 

 

▶ file.service.ts

@Injectable()
export class FileService {
  constructor() {}

  fileUpload(file: Express.Multer.File) {
    if (!file) {
      throw new BadRequestException();
    }

    return file.path;
  }
}
  • Multer를 통해 업로드된 파일이 정상 업로드 되었는지 확인 후 경로를 반환해주는 서비스를 작성하였다.

 

 

 

▶ file.controller.ts

@Controller('file')
export class FileController {
  constructor(private readonly fileService: FileService) {}

  @UseInterceptors(FileInterceptor('file'))
  @Post('upload/single')
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    console.log(file);
    return this.fileService.fileUpload(file);
  }

}
  • 방금 만든 서비스를 사용하여 클라이언트에게 응답하는 코드를 추가하였다.

 

 


결과

 

저장된 파일의 경로를 클라이언트에게 반환해주었다.

 

 

Console로 출력했을 때 파일에 대한 인포가 찍히는 것을 확인할 수 있다. Multer에서 요청 핸들러로 오기 전에 파일 업로드 및 서버에 저장 처리를 했으므로 경로, 파일명 등이 함께 출력되는 것을 확인할 수 있다.

 

 

서버 디렉토리에 이미지가 저장되었다.

 

 

 

 

 

 

 


 

Reference

https://docs.nestjs.com/techniques/file-upload

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

 

https://devkkiri.com/post/731965a8-517f-4856-b94a-c17d9e08a16c

 

NestJS 파일 업로드하기(1) | Kkiri Blog

NestJS에서 파일 업로드하는 것은 expre...

devkkiri.com

 

반응형

BELATED ARTICLES

more