[NestJS] 파일업로드 & 다운로드 처리(프로젝트 외부 디렉토리에 파일 저장하기)

module 및 controller 생성

nest g mo common/file
nett g co common/file --no-spec

common/file.moudle.ts

기본 설정으로 했을때 파일 업로드시 프로젝트 내부 폴더에 파일이 업로드가 되버려서 파일 관리가 어렵다.

그래서 이왕 하는김에 파일업로드 위치를 설정파일에서 설정하도록 하고,

NestJs ConfigService 로 설정값을 가져다 MulterModule 을 설정할 수 있도록 해 보자.

.env 파일에 요렇게 설정하고

ATTACH_SAVE_PATH=c:\file\cms

NestJs config 설정을 해준다. NestJs config 설정방법은 요글 참고.

common/file.moudle.ts은 다음과 같다.

import { Module } from '@nestjs/common';
import { FileController } from './file.controller';
import { MulterModule } from '@nestjs/platform-express';
import { ConfigModule, ConfigService } from '@nestjs/config';

import { diskStorage } from 'multer';
// 오늘날짜 포맷팅 쉽게 할려고 가볍게 쓸 수 있는것 하나 설치해봄
// npm i light-date
import { format } from 'light-date';
import { extname } from 'path';

import * as fs from 'fs';

@Module({
    imports: [
        // ConfigService 를 inject 하기 위해
        MulterModule.registerAsync({
            imports: [ConfigModule],
            useFactory: async (config: ConfigService) => ({
                storage: diskStorage({
                    destination: function (req, file, cb) {
                        // 파일저장위치 + 년월 에다 업로드 파일을 저장한다.
                        // 요 부분을 원하는 데로 바꾸면 된다.
                        const dest = `${config.get('ATTACH_SAVE_PATH')}/${format(new Date(), '{yyyy}{MM}')}/`;

                        if (!fs.existsSync(dest)) {
                            fs.mkdirSync(dest, { recursive: true });
                        }

                        cb(null, dest);
                    },
                    filename: (req, file, cb) => {
                        // 업로드 후 저장되는 파일명을 랜덤하게 업로드 한다.(동일한 파일명을 업로드 됐을경우 오류방지)
                        const randomName = Array(32)
                            .fill(null)
                            .map(() => Math.round(Math.random() * 16).toString(16))
                            .join('');
                        return cb(null, `${randomName}${extname(file.originalname)}`);
                    },
                }),
            }),
            inject: [ConfigService],
        }),
    ],
    controllers: [FileController],
})
export class FileModule {}

common/file.controller.ts

import { Controller, Get, Post, Param, UseInterceptors, UploadedFile, Res, Query } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Response } from 'express';

import { ConfigService } from '@nestjs/config';

@Controller('file')
export class FileController {
    constructor(private config: ConfigService) {}

    // 파일 업로드
    // http://localhost:3000/file
    @Post()
    @UseInterceptors(FileInterceptor('file'))
    create(@UploadedFile() file) {
        const path = file.path.replace(this.config.get('ATTACH_SAVE_PATH'), '');

        // 원본파일명과 저장된 파일명을 리턴해서 다운로드 받을때 씀
        return {
            fileName: file.originalname,
            savedPath: path.replace(/\\/gi, '/'),
            size: file.size,
        };
    }

    // 파일 다운로드
    // http://localhost:3000/file/[savedPath]?fn=[fileName]
    // http://localhost:3000/file/202104/12312541515151.xlsx?fn=다운받을원본파일명.xlsx
    @Get(':path/:name')
    async download(@Res() res: Response, @Param('path') path: string, @Param('name') name: string, @Query('fn') fileName) {
        res.download(`${this.config.get('ATTACH_SAVE_PATH')}/${path}/${name}`, fileName);
    }
}