ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Multer와 미디어 파일 서비스(img,mp3,mp4) 1
    FrameWork/Nest.js 2022. 7. 30. 10:42

    File upload by Multer

    Nest.js에서 파일을 upload하려면 multer 미들웨어 패키지를 사용해야 한다. multer는 주로 HTTP post 요청을 통해서 파일을 업로드 한다. multipart/form-data 형식으로 게시된 데이터를 처리한다.

     

    *multipart/form-data : HTTP 통신방식에서 binary 데이터를 주고 받을때 사용하는 form 이다.

     

    Header- ContentType

    보통  API로 data를 주고 받을 때 Content-Type은 위 그림과 같다.

     

    단일 file example

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

    단일 파일을 업로드 하려면 FileInterceptor()인터셉터를 라우트 핸들러에 연결하고 @UploadedFile()데토레이터를 사용하여 Request에서 file을 추출하면 된다.

     

    FileInterceptor() 는 두개의 인자를 받는다 =>

    • fieldName : 파일이 첨부되어 있는 HTML form에서 필드 이름을 제공하는 문자열
    • options : MulterOptions 타입을 선택할 수 있는 객체로 multer 생성자에서 사용하는 것과 동일한 객체이다.

     

    Array of files

    @Post('upload')
    @UseInterceptors(FilesInterceptor('image'))
    uploadFile(@UploadedFiles() files: Array<Express.Multer.File>) {
      console.log(files);
    }

    파일의 배열(단일 필드 이름으로 식별된다.)을 업로드 하려면 FilesInterceptior()를 사용한다. FilesInterceptior()의 인자  'image'는 프론트엔드에서 보내는 file의 이름과 동일해야 하고, 전달 되는 형태는  binary file이다. 

     

    또 해당 Interceptors와 매칭된는 매서드는 @UploadedFiles()라는 데코레이터를 통해서 file들을(rquest에서 파일을 추출) 받을 수있다.

     

    FilesInterceptor() 는 세가지 인자를 받는다  =>

    • fieldName : 위와 동일
    • maxCount : 허용가능한 최대의 file수를 지정하는 인자이다.
    • options : MulterOptions 위와 동일

    +cats.module에서 MulterModule을 import하는 것을 잊지않는다.

     MulterModule.register({
          dest: './upload',
        }),
        MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }]),
        forwardRef(() => AuthModule),
      ],

    여기서 dest는 destination의 약자로 최종적으로 file이 저장되는 경로를 가르킨다.

     

     

    서버 폴더로 돌아오면, upload 폴더가 생성된 것을 확인 할 수 있다. 열어보면 이상한 문자열로된 파일이 들어 있는 것을 확인 할 수 있다. 하지만 이것 보단 프론트에서 image를 upload하면 백엔드의 image를 upload하는 폴더에 해당하는 image가 담기는 것이 목적이다.

     

    multerOptions

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

    여기서 multerOptions는 FilesInterceptior()에 두번째 인자로 들어가는 option이다. 인자를 폴더로 받을 것인데, upload하는 폴더안에 폴더를 만들어서 받아 줄 것이다. 

     

    이것은 api를 분리하기 위함인데, 가령 고양이 사진 같은 경우는 cat 폴더를 만들어서특정 Api경로로 들어온 image만 cat 폴더에 저장하는 것이다. 이는  특정한 목적이 있는 image를 올리때 목적에 맞는 image끼리 저장을 해서 image를 분리하는 위한 것이다.

     

    multerOption 변수를 생성하는 메서드는 upload  폴더안의 어떤 폴더에 upload된 image를 저장할지 문자열 인자로 값을 받는다.

     

     

     

    createFolder()

    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...`);
      }
    };

     

    fs.mkdirSync()

    fs.mkdirSync(path.join(__dirname, '..', `uploads`));
     fs.mkdirSync(path.join(__dirname, '..', `uploads/${folder}`));

    fs.mkdirSync()는 폴더를 만드는 명령 메서드이다.

     

     

    path.join()

    path.join(__dirname, '..', `uploads`)

    현재 폴더 경로(__dirname)부모 폴더로 가서('..')uploads라는 폴더를 만들라는 의미(`uploads`)의 명령을 하는 메서드이다.

     

    try~catch문에서 error가 발생하면 이미 해당 폴더가 존재하는 것이다. 첫번째 try~catch문에서는 사진을 upload하는 upload 폴더를 만들고, 두번째 try~catch문에서는 upload 폴더안에 특정 요청 api와 매칭될 사진의 카테고리가 될 폴더를 만든다. 그래서 폴더 이름은 변수로 받은 folder가 된다.

     

    storage : storage()

    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);
        },
      });
    };

    또 MulterOption에는 storage라는 속성이 존재하는데 storage() 메서드를 정의해서 값을 넣어준다.

     

     

    multer.diskStorage({})

     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);
        },
      });

    createFolder()를 통해서 folder를 만든 후에  multer.diskStorage({})의 option인 destination filename을 통해서 image를 어디에 저장할지, 어떤 이름으로 저장할 지를 정한뒤에 해당 내용을 return해준다.

    file이 들어온뒤에 저장할 경로는 createFolder()에서 두번째 try~catch문에 있는 것과 같다. 그렇게 구한 filename을 cd()의 두번째 인자로 넣어준다.
     

    path.extname()

    path.extname('index.html')
    //Retruns: '.html'
    그리고file을 저장할 이름을 얻기위해 우선 PlatformPath의 메서드인 extname()을 사용해서 upload한 file을 올렸을때 file의 name을 읽어서 file의 확장자를 추출한다. ext의 값은 png,jpg,mp4 같은 것들일 것이다.
     

    fileName

     const fileName = `${path.basename(
            file.originalname,
    
            ext,
          )}${Date.now()}${ext}`;

     

    마지막으로 최종적으로 저장할 fileName을 값으로 해서 해당 변수에 저장한다. 만약 file을 올렸을때 기존에 똑같은 이름의 file있다면 error가 발생 할 것이다. 그렇기 때문에 file을 upload할 때 그때 시각을 찍은뒤 그것을 뒤에 이어 붙인뒤에 file이름에 포함시킨다.

     

    그리고 얻어낸 fileName역시 cd()의 두번째 인자로 넣어준다.

     

    정리

    위와 같은 함수를 겉쳐서 얻어낸 값을 최종적으로 multerOption에 넣어 사용한다. 즉 multerOptions이라는 함수는 folder를 받는데 parameter인 folder의 값이 무엇인지에 따라 upload된 file을 지정된 폴더에 넣어주는 함수이다.

     

    [출처 -https://www.inflearn.com/course/%ED%83%84%ED%83%84%ED%95%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%84%A4%EC%8A%A4%ED%8A%B8/dashboard]

     
     
     
     

    'FrameWork > Nest.js' 카테고리의 다른 글

    Next.js custom decorator만들기  (0) 2022.08.07
    Nest.js 커스텀 Middleware 만들기  (0) 2022.08.04
    Nest.js Intercepters(AOP 패턴)  (0) 2022.07.24
    Nest.js Pipes(변환, 유효성 검사)  (0) 2022.07.11
    Nest.js Exception Filter  (0) 2022.07.11

    댓글

Designed by Tistory.