Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1aa6f2c
feat ( #10 ) : AwsConfig
coehgns Jul 30, 2025
fd59d9f
feat ( #10 ) : FilterConfig
coehgns Jul 30, 2025
e34394e
feat ( #10 ) : SecurityConfig
coehgns Jul 30, 2025
05588ec
feat ( #10 ) : BaseEntity
coehgns Jul 30, 2025
ce61323
feat ( #10 ) : BaseTimeEntity
coehgns Jul 30, 2025
ff6c83a
feat ( #10 ) : BaseUUIDEntity
coehgns Jul 30, 2025
9a24c0f
feat ( #10 ) : CasperException
coehgns Jul 30, 2025
a3b50d2
feat ( #10 ) : ErrorCode
coehgns Jul 30, 2025
fa8d7c5
feat ( #10 ) : ErrorResponse
coehgns Jul 30, 2025
aa391c2
feat ( #10 ) : GlobalExceptionFilter
coehgns Jul 30, 2025
361159c
feat ( #10 ) : GlobalExceptionHandler
coehgns Jul 30, 2025
d63d976
feat ( #10 ) : ExpiredTokenException
coehgns Jul 30, 2025
c7e5a01
feat ( #10 ) : InternalServerErrorException
coehgns Jul 30, 2025
e5545d4
feat ( #10 ) : InvalidTokenException
coehgns Jul 30, 2025
a81e0d8
feat ( #10 ) : JwtFilter
coehgns Jul 30, 2025
c186db6
feat ( #10 ) : JwtProperties
coehgns Jul 30, 2025
106b739
feat ( #10 ) : AdminUtils
coehgns Aug 1, 2025
4111ac5
feat ( #10 ) : UserUtils
coehgns Aug 1, 2025
c4b257d
feat ( #10 ) : KafkaConsumerConfig
coehgns Aug 1, 2025
6b07954
feat ( #10 ) : KafkaProperty
coehgns Aug 1, 2025
d4562ec
feat ( #10 ) : KafkaTopics
coehgns Aug 1, 2025
105e190
feat ( #10 ) : DeleteFaqTableConsumer
coehgns Aug 1, 2025
00a4cca
feat ( #10 ) : BadFileExtensionException
coehgns Aug 1, 2025
e1899fd
feat ( #10 ) : EmptyFileException
coehgns Aug 1, 2025
56b8b73
feat ( #10 ) : FileUtil
coehgns Aug 1, 2025
9a2f3c0
feat ( #10 ) : PathList
coehgns Aug 1, 2025
add59a3
feat ( #10 ) : 커밋 못한 AttachFile
coehgns Aug 1, 2025
b4cb0ad
refactor ( #10 ) : 중복된 build 제거
coehgns Aug 2, 2025
9a2725e
refactor ( #10 ) : BaseEntity UUID 전략 수정
coehgns Aug 2, 2025
b08a71f
refactor ( #10 ) : FileUtil 파일 확장자 검증 로직 리팩토링
coehgns Aug 2, 2025
03dbf1f
refactor ( #10 ) : GlobalExceptionHandler 메서드 네이밍 수정
coehgns Aug 2, 2025
24eae29
refactor ( #10 ) : GlobalExceptionHandler kdoc 수정
coehgns Aug 2, 2025
9934254
refactor ( #10 ) : FileUtil의 verificationFile 메서드의 빈문자열이거나 공백만 있는 경우도…
coehgns Aug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package hs.kr.entrydsm.feed.infrastructure.s3.util

import com.amazonaws.HttpMethod
import com.amazonaws.services.s3.AmazonS3
import com.amazonaws.services.s3.model.CannedAccessControlList
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest
import com.amazonaws.services.s3.model.ObjectMetadata
import com.amazonaws.services.s3.model.PutObjectRequest
import hs.kr.entrydsm.feed.infrastructure.s3.exception.BadFileExtensionException
import hs.kr.entrydsm.feed.infrastructure.s3.exception.EmptyFileException
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.MediaType
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import java.io.ByteArrayInputStream
import java.io.InputStream
import java.util.*

/**
* AWS S3와의 상호작용을 추상화한 유틸리티 클래스입니다.
*
* 이 클래스는 파일 업로드, 다운로드 URL 생성, 파일 삭제 등 S3와 관련된
* 공통적인 작업을 처리하기 위한 유틸리티 메서드를 제공합니다.
*
* @property amazonS3 AWS S3 클라이언트 인스턴스
* @property bucketName S3 버킷 이름 (application.yml에서 주입됨)
*
* @throws EmptyFileException 업로드할 파일이 비어있는 경우 발생
* @throws BadFileExtensionException 허용되지 않은 파일 확장자인 경우 발생
*/
@Service
class FileUtil(
private val amazonS3: AmazonS3,
) {
@Value("\${cloud.aws.s3.bucket}")
lateinit var bucketName: String
Comment thread
coehgns marked this conversation as resolved.

companion object {
const val EXP_TIME = 10000 * 60 * 2
}

/**
* 파일을 S3에 업로드하고 생성된 파일명을 반환합니다.
*
* @param file 업로드할 파일
* @param path S3 버킷 내 저장 경로
* @return 생성된 랜덤 파일명 (확장자 포함)
* @throws EmptyFileException 파일이 비어있는 경우
* @throws BadFileExtensionException 허용되지 않은 확장자인 경우
*/
fun upload(
file: MultipartFile,
path: String,
): String {
val ext = verificationFile(file)

val randomName = UUID.randomUUID().toString()
val filename = "$randomName.$ext"
val inputStream: InputStream = ByteArrayInputStream(file.bytes)

val metadata =
ObjectMetadata().apply {
contentType =
when (ext) {
"pdf" -> MediaType.APPLICATION_PDF_VALUE
else -> MediaType.IMAGE_PNG_VALUE
}
contentLength = file.size
contentDisposition = "inline"
}

inputStream.use {
amazonS3.putObject(
PutObjectRequest(bucketName, "${path}$filename", it, metadata)
.withCannedAcl(CannedAccessControlList.AuthenticatedRead),
)
}

return filename
}

/**
* S3에서 지정된 파일을 삭제합니다.
*
* @param objectName 삭제할 파일명
* @param path 파일이 위치한 S3 경로
*/
fun delete(
objectName: String,
path: String,
) {
amazonS3.deleteObject(bucketName, path + objectName)
}

/**
* S3에 저장된 파일에 접근할 수 있는 임시 URL을 생성합니다.
*
* @param fileName 접근할 파일명
* @param path 파일이 위치한 S3 경로
* @return 임시 접근 URL
*/
fun generateObjectUrl(
fileName: String,
path: String,
): String {
val expiration =
Date().apply {
time += EXP_TIME
}
return amazonS3.generatePresignedUrl(
GeneratePresignedUrlRequest(
bucketName,
"${path}$fileName",
).withMethod(HttpMethod.GET).withExpiration(expiration),
).toString()
}

/**
* 업로드할 파일의 유효성을 검증하고 파일 확장자를 반환합니다.
*
* @param file 검증할 파일
* @return 소문자로 변환된 파일 확장자
* @throws EmptyFileException 파일이 비어있는 경우
* @throws BadFileExtensionException 허용되지 않은 확장자인 경우
*/
private fun verificationFile(file: MultipartFile): String {
if (file.isEmpty || file.originalFilename == null) throw EmptyFileException
Comment thread
coehgns marked this conversation as resolved.
Outdated
val originalFilename = file.originalFilename!!
val ext = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).lowercase(Locale.getDefault())
Comment thread
coehgns marked this conversation as resolved.
Outdated

if (!(
ext == "jpg" || ext == "jpeg" ||
ext == "png" || ext == "heic" ||
ext == "hwp" || ext == "pptx" ||
ext == "pdf" || ext == "xls" ||
ext == "xlsx"
)
) {
throw BadFileExtensionException
}
Comment thread
coehgns marked this conversation as resolved.
Outdated

return ext
}
}