multipart-form 데이터

미디어 파일을 보낼 때는 어떻게 해야하는가.

content-type 이라고

데이터를 보낼 때 헤더에 설정하는 부분이 있다.

여기를 multipart-form 데이터로 설정해야 한다.

multer 패키지 설치

npm i -D @types/multer

단일 파일을 사용하기 위해서는..

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
  console.log(file);
}

위와 같은 인터셉터를 사용하라고 되어있음.

  @ApiOperation({ summary: '이미지 업로드' })
  @Post('upload')
  @UseInterceptors(FileInterceptor('files'))
  uploadCatImg(@UploadedFiles() files: Array<Express.Multer.File>) {
    console.log(files);
    return 'uploadImg';
  }

위와 같이 사용하면 file들의 배열 형태로 받을 수 있게 된다.

또한 Module에 import 시켜줘야 한다.

MulterModule.register({dest: './upload'}),

dest 는 destination 목적지 url 을 입력해줌.

upload 해보면…

{success: true, data: "uploadImg"}

위와 같이 뜨는 것을 확인할 수 있다.

그러면 서버 폴더의 upload 폴더 안에 이미지가 들어가는 것을 확인할 수 있다.

Alt text

multer options

import * as multer from 'multer';
import * as path from 'path';
import * as fs from 'fs';
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';

const createFolder = (folder: string) => {
  try {
    console.log('💾 Create a root uploads folder...');

    fs.mkdirSync(path.join(__dirname, '..', `uploads`));
  } catch (error) {
    console.log('The folder already exists...');
  }

  try {
    console.log(`💾 Create a ${folder} uploads folder...`);

    fs.mkdirSync(path.join(__dirname, '..', `uploads/${folder}`));
  } catch (error) {
    console.log(`The ${folder} folder already exists...`);
  }
};

const storage = (folder: string): multer.StorageEngine => {
  createFolder(folder);

  return multer.diskStorage({
    destination(req, file, cb) {
      //* 어디에 저장할 지
      const folderName = path.join(__dirname, '..', `uploads/${folder}`);
      cb(null, folderName);
    },

    filename(req, file, cb) {
      //* 어떤 이름으로 올릴 지
      const ext = path.extname(file.originalname);
      const fileName = `${path.basename(
        file.originalname,
        ext,
      )}${Date.now()}${ext}`;
      cb(null, fileName);
    },
  });
};

export const multerOptions = (folder: string) => {
  const result: MulterOptions = {
    storage: storage(folder),
  };

  return result;
};

위 코드를 common/utils/multer.options.ts 안에 넣어둔다.

폴더 만들어주고, 파일 명 만들어주는 로직임.

@UseInterceptors(FilesInterceptor('image', 10, multerOptions("cats")))

최대 10개까지 된다.

일단 이미지가 서버 프로그램 경로에 들어가는 것을 확인할 수 있다.

MiddleWare를 만들어주기

데이터베이스에는 파일의 경로명이 들어가있어야 한다.

그 경로명을 넣어주기 위해 Middleware를 만들어준다.

먼저 main.ts 에서

app.useStaticAssets(path.join(__dirname, './common', 'uploads'), {
    prefix: '/media',
});

여기에서 useStaticAssets 가 없다고 에러가 뜰 것이다. 얘는 express application이라고 명시해줘야한다.

const app = await NestFactory.create<NestExpressApplication>(AppModule);

위의 prefix: ‘media’ 에 대하여…

이렇게 함으로써, // http://localhost:8000/media/cats/aaa.png 에 접근하여 해당 파일을 가져올 수 있게 해주는 것이다.

이제 마지막으로

controller 에서

@ApiOperation({ summary: '이미지 업로드' })
@Post('upload')
@UseInterceptors(FilesInterceptor('image', 10, multerOptions("cats")))
uploadCatImg(@UploadedFiles() files: Array<Express.Multer.File>) {
  console.log(files);
  // return 'uploadImg';

  return { images: `http://localhost:8000/media/cats/${files[0].filename}` };
}

일단 0번째 인덱스에 있는 filename 을 기반으로 저장시켜준다.

스키마에 default 사진 가져오도록 수정

@Prop({
  default:
    'https://raw.githubusercontent.com/amamov/teaching-nestjs-a-to-z/main/images/1.jpeg',
})
@IsString()
imgUrl: string;

그리고, readonlydata에 imgURL 추가

readonly readOnlyData: {
  id: string;
  email: string;
  name: string;
  imgUrl: string;
};
CatSchema.virtual('readOnlyData').get(function (this: Cat) {
  return {
    id: this.id,
    email: this.email,
    name: this.name,
    imgUrl: this.imgUrl,
  };
});

service에 있는 메서드 가져올 것이다.

@ApiOperation({ summary: '이미지 업로드' })
@UseInterceptors(FilesInterceptor('image', 10, multerOptions("cats")))
@UseGuards(JwtAuthGuard)
@Post('upload')
uploadCatImg(@UploadedFiles() files: Array<Express.Multer.File>, @CurrentUser() cat: Cat) {
  console.log(files);
  // return 'uploadImg';
  // return { images: `http://localhost:8000/media/cats/${files[0].filename}` };

  return this.catsService.uploadImg(cat, files);
}
async uploadImg(cat: Cat, files: Express.Multer.File[]) {
  const fileName = `cats/${files[0].filename}`;
  console.log(fileName);
  const newCat = await this.catsRepository.findByIdAndUpdateImg(
    cat.id,
    fileName,
  );
  console.log(newCat);
  return newCat;
}

위와 같이 구현

이제 폴더 관리를 좀 해줘야 해서 controller를 일단 빼서 정리하였음.

보너스 POSTMAN에서 파일업로드 하기

form data 라는 애에서 file로 설정하면 파일을 올릴 수 있게 되어있음