This repository has been archived by the owner on Dec 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implements object-storage module
- Loading branch information
1 parent
5f016fe
commit 26955c6
Showing
15 changed files
with
1,169 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './time.constants' | ||
export * from './storage.constants' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const IMAGE_SIZE_LIMIT = 10 * 1024 * 1024 // 10mb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { ConfigService } from '@nestjs/config' | ||
import { | ||
DeleteObjectCommand, | ||
PutObjectCommand, | ||
S3Client | ||
} from '@aws-sdk/client-s3' | ||
import { Service } from '@libs/decorator' | ||
import { | ||
ParameterValidationException, | ||
UnexpectedException | ||
} from '@libs/exception' | ||
import mime from 'mime-types' | ||
import { v4 as uuidv4 } from 'uuid' | ||
import type { ImageStorageService } from './storage.interface' | ||
|
||
@Service() | ||
export class ImageStorageServiceImpl implements ImageStorageService { | ||
private readonly s3: S3Client | ||
|
||
constructor(private readonly configService: ConfigService) { | ||
if (this.configService.get('NODE_ENV') === 'production') { | ||
this.s3 = new S3Client() | ||
} else { | ||
this.s3 = new S3Client({ | ||
region: this.configService.get('AWS_CDN_BUCKET_REGION'), | ||
endpoint: this.configService.get('AWS_CDN_BUCKET_URL'), | ||
forcePathStyle: true, | ||
credentials: { | ||
accessKeyId: this.configService.get('AWS_CDN_ACCESS_KEY') || '', | ||
secretAccessKey: this.configService.get('AWS_CDN_SECRET_KEY') || '' | ||
} | ||
}) | ||
} | ||
} | ||
|
||
async uploadObject( | ||
file: Express.Multer.File, | ||
src: string | ||
): Promise<{ src: string }> { | ||
try { | ||
const extension = this.getFileExtension(file.originalname) | ||
const keyWithoutExtenstion = `${src}/${this.generateUniqueImageName()}` | ||
const key = keyWithoutExtenstion + `${extension}` | ||
const fileType = this.extractContentType(file) | ||
|
||
await this.s3.send( | ||
new PutObjectCommand({ | ||
Bucket: this.configService.get('AWS_CDN_BUCKET_NAME'), | ||
Key: key, | ||
Body: file.buffer, | ||
ContentType: fileType | ||
}) | ||
) | ||
|
||
if (this.configService.get('NODE_ENV') === 'production') { | ||
return { | ||
src: keyWithoutExtenstion | ||
} | ||
} else { | ||
return { | ||
src: key | ||
} | ||
} | ||
} catch (error) { | ||
throw new UnexpectedException(error) | ||
} | ||
} | ||
|
||
async deleteObject(src: string): Promise<{ result: string }> { | ||
try { | ||
await this.s3.send( | ||
new DeleteObjectCommand({ | ||
Bucket: this.configService.get('AWS_CDN_ORIGIN_BUCKET_NAME'), | ||
Key: src | ||
}) | ||
) | ||
|
||
return { result: 'ok' } | ||
} catch (error) { | ||
throw new UnexpectedException(error) | ||
} | ||
} | ||
|
||
/** | ||
* 랜덤한 uuid를 생성하여 리턴합니다. | ||
* | ||
* @returns {string} | ||
*/ | ||
private generateUniqueImageName(): string { | ||
const uniqueId = uuidv4() | ||
|
||
return uniqueId | ||
} | ||
|
||
/** | ||
* 파일이름으로 부터 MimeType을 추출하여 리턴합니다. | ||
* @param {Express.Multer.File} file - MimeType을 추출할 파일 | ||
* @returns {string} 추출한 MimeType | ||
*/ | ||
private extractContentType(file: Express.Multer.File): string { | ||
if (file.mimetype) { | ||
return file.mimetype.toString() | ||
} | ||
|
||
return file.originalname | ||
? mime.lookup(file.originalname) || 'application/octet-stream' | ||
: 'application/octet-stream' | ||
} | ||
|
||
/** | ||
* 파일에서 확장자를 추출합니다. | ||
* | ||
* @param {string} filename - 파일 원본명 | ||
* @returns {string} 확장자 | ||
* @throws {ParameterValidationException} 전달받은 파일에 확장자가 없는 경우 발생 | ||
*/ | ||
private getFileExtension(filename: string): string { | ||
const match = filename.match(/\.[^.]+$/) | ||
|
||
if (match) { | ||
return match[0] | ||
} | ||
|
||
throw new ParameterValidationException('Unsupported file extension') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './options/image-options' | ||
export * from './storage.module' | ||
export * from './image-storage.service' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface' | ||
import { IMAGE_SIZE_LIMIT } from '@libs/constants' | ||
import type { FileFilterCallback } from 'multer' | ||
|
||
export const IMAGE_OPTIONS: MulterOptions = { | ||
limits: { | ||
fieldSize: IMAGE_SIZE_LIMIT | ||
}, | ||
fileFilter: ( | ||
req: Request, | ||
file: Express.Multer.File, | ||
cb: FileFilterCallback | ||
) => { | ||
const fileExts = ['png', 'jpg', 'jpeg', 'webp', 'heif', 'heic'] | ||
const ext = file.originalname.split('.').pop().toLocaleLowerCase() | ||
if (!fileExts.includes(ext)) { | ||
return cb(null, false) | ||
} | ||
cb(null, true) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
export interface StorageService { | ||
/** | ||
* 파일을 지정한 경로에 저장하고 저장된 파일이름을 포함한 전체 경로를 반환합니다. | ||
* | ||
* @param {Express.Multer.File} file - 저장할 파일 | ||
* @param {string} src - 파일을 저장할 경로 | ||
* @returns {Promise<{src: string}>} 파일이 저장된 경로 | ||
*/ | ||
uploadObject(file: Express.Multer.File, src: string): Promise<{ src: string }> | ||
|
||
/** | ||
* 해당 URI에 있는 파일을 삭제하고 삭제 결과를 반환합니다. | ||
* | ||
* @param {string} src - 삭제할 파일이 위치한 경로 | ||
* @returns {Promise<{result: string}>} 파일 삭제 결과 | ||
*/ | ||
deleteObject(src: string): Promise<{ result: string }> | ||
} | ||
|
||
export interface ImageStorageService extends StorageService {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Global, Module } from '@nestjs/common' | ||
import { ImageStorageServiceImpl } from './image-storage.service' | ||
|
||
@Global() | ||
@Module({ | ||
providers: [ | ||
{ | ||
provide: 'ImageStorageService', | ||
useClass: ImageStorageServiceImpl | ||
} | ||
], | ||
exports: [ | ||
{ | ||
provide: 'ImageStorageService', | ||
useClass: ImageStorageServiceImpl | ||
} | ||
] | ||
}) | ||
export class StorageModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"compilerOptions": { | ||
"declaration": true, | ||
"outDir": "../../dist/libs/storage" | ||
}, | ||
"include": ["src/**/*"], | ||
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.