Today I learned
nestjs를 이용해서 이미지를 업로드 하는 작업을 진행했다
expressjs에서는 multer패키지를 사용해서 이미지를 업로드했다
nestjs에서는 multer를 기본적으로 nestjs에 내장되어 있다고 한다 @nestjs/platform-express 패키지를 사용하자
작업을 진행하기 전 안전한 개발을 위해 설치해야하는 파일이 있다
npm install @types/multer --save-dev
image.module에다가 설정을 진행해줘야 한다
@Module({
imports: [
MulterModule.registerAsync({
useFactory: multerOptionsFactory,
}),
],
controllers: [ImageController],
providers: [ImageService],
})
export class ImageModule {}
register를 사용하지 않고 registerAsync를 사용하는 이유는 다음 작업에서 AWS S3와 연동하기 위해서 configService를 inject하기 위해서다
multerOptionsFactory를 만들자 처음은 디스크 방식의 업로드로 진행했다
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
import { diskStorage, memoryStorage } from 'multer';
import { extname, basename } from 'path';
export const multerOptionsFactory = (): MulterOptions => {
return {
storage: diskStorage({
destination: (request, file, callback) => {
const uploadPath = 'uploads';
if (!existsSync(uploadPath)) {
mkdirSync(uploadPath);
}
callback(null, uploadPath);
},
filename(req, file, cb) {
const imageExt = extname(file.originalname);
const imageBasename = basename(file.originalname, imageExt);
cb(null, `${imageBasename}_${Date.now()}${imageExt}`);
},
}),
};
};
storage: diskStorage를 사용할 때 destination 옵션을 사용하면 미들웨어를 지나가면서 바로 업로드가 진행이 된다 이 부분이 편하기는 하지만 내가 생각하는 동작방식과 어울리지 않는다
나는 이미지와 같이 요청들어오는 데이터들이 DB에 안정적으로 저장이 되고 요청이 정상적으로 작동을 했다면 마지막에 이미지를 저장하고 싶었다
저번 작업에서는 요청이 들어오고 예외처리가 진행되었는데 이미지는 저장이 되어서 다시 삭제하는 문제가 있었기 때문에 이 부분에서 꼭 해결하고 싶었다
그래서 destination옵션을 사용하지 않고 진행을 해보았다 사용하지 않으면 운영체제의 시스템 임시 파일을 저장하는 기본 디렉터리에 임시 저장이 된다
이제 image.controller를 가보자
@Controller('image')
export class ImageController {
constructor(private readonly imageService: ImageService) {}
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
return this.imageService.uploadImage(uuid(), file);
}
}
@UseInterceptors(FileInterceptor(’file’) 이 부분에서 이미지를 보낼 때 key 값으로 지정한 이름을 잘 적용해 주어야 한다
나는 파일을 service에서 처리하기 때문에 요청받은 파일을 service에 보낸다
다음 image.service에 가보자
@Injectable()
export class ImageService {
uploadImage(userId: string, file: Express.Multer.File) {
if (!file) {
throw new BadRequestException('이미지가 존재하지 않습니다.');
}
const uploadFilePath = `uploads/${userId}`;
console.log(existsSync(uploadFilePath));
if (!existsSync(uploadFilePath)) {
// uploads 폴더가 존재하지 않을시, 생성합니다.
mkdirSync(uploadFilePath, { recursive: true });
}
const fileName = Date.now() + extname(file.originalname);
const uploadPath = __dirname + `/../../${uploadFilePath + '/' + fileName}`;
writeFileSync(uploadPath, file.path);
}
}
이미지의 이름과 업로드 경로를 지정하고 writeFileSync를 이용해서 임시 저장소에 있는 파일을 저장하는 방식으로 진행을 했는데 문제가 발생했다 이미지가 저장은 되는데 이미지가 깨진건지 로드가다
writeFileSync를 조금 더 알아본 결과 data에 들어가는 것들이 파일에 기록될 문자열이나 Buffer, TypedArray, DataView라고 한다 그래서 file.path를 readFileSync로 파일을 읽어서 buffer정보를 반환시켜 사용해보기로 했다
writeFileSync(uploadPath, readFileSync(file.path));
이렇게 사용하면 이미지가 이상하게 저장되지 않고 정상적으로 저장이 된다!
buffer를 이용해서 저장하기 때문에 임시파일을 저장하고 그 저장된 임시파일을 읽어서 buffer를 다시 사용하는 것 보다 임시파일을 생성하기 않고 메모리에 있는 정보를 사용하는 방식이 더 효율적일 수 있겠다는 생각이 들어서 메모리를 사용하는 방식으로도 진행해 보았다
multer.options.factory.ts 파일로 돌아가서 multerOptionsFactory를 수정해보자
return {
storage: memoryStorage(),
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB로 크기를 제한
};
storage: memoryStorage를 사용하는 방식이다
image.service.ts 파일을 가서 writeFileSync를 수정하자
writeFileSync(uploadPath, file.buffer);
이 방식을 사용해도 정상적으로 작동을 한다
참고 사이트 / 블로그
'과거공부모음' 카테고리의 다른 글
20230227 TIL - 최종프로젝트 (찰칵), nestjs-form-data (0) | 2023.02.27 |
---|---|
20230222 TIL - nestjs AWS S3 file upload (2) | 2023.02.22 |
나의 개발일지 TIL(Today I learned) - 트랜잭션, 동시성 제어 (0) | 2023.02.17 |
나의 개발일지 20220213 TIL(Today I learned) - NestJS, Token 응답 (0) | 2023.02.13 |
나의 개발일지 20220206 TIL(Today I learned) - 미니프로젝트 (0) | 2023.02.06 |