Files
blog-backend/src/main/kotlin/me/wypark/blogbackend/domain/image/ImageService.kt
pwy3282040@msecure.co 884853586d feat: 댓글 구조 변경
작성자 표시를 위해 CommentResponse에 isPostAuthor 필드 추가
2025-12-27 00:14:57 +09:00

72 lines
2.5 KiB
Kotlin

package me.wypark.blogbackend.domain.image
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import software.amazon.awssdk.core.sync.RequestBody
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.GetUrlRequest
import software.amazon.awssdk.services.s3.model.PutObjectRequest
import java.util.*
@Service
class ImageService(
private val s3Client: S3Client,
@Value("\${spring.cloud.aws.s3.endpoint:http://minio:9000}") private val endpoint: String
) {
private val bucketName = "blog-images" // 버킷 이름
init {
createBucketIfNotExists()
}
fun uploadImage(file: MultipartFile): String {
// 1. 파일명 중복 방지 (UUID 사용)
val originalName = file.originalFilename ?: "image.jpg"
val ext = originalName.substringAfterLast(".", "jpg")
val fileName = "${UUID.randomUUID()}.$ext"
// 2. S3(MinIO)로 업로드
val putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.contentType(file.contentType)
.build()
s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.inputStream, file.size))
// 3. 접속 가능한 URL 반환
// 로컬 개발 환경에서는 localhost 주소를 직접 조합해서 줍니다.
// 배포 시에는 실제 도메인이나 CloudFront 주소로 변경해야 합니다.
return "$endpoint/$bucketName/$fileName"
}
private fun createBucketIfNotExists() {
try {
// 버킷 존재 여부 확인 (없으면 에러 발생하므로 try-catch)
s3Client.headBucket { it.bucket(bucketName) }
} catch (e: Exception) {
// 버킷 생성
s3Client.createBucket { it.bucket(bucketName) }
// ⭐ 버킷을 Public(공개)으로 설정 (이미지 조회를 위해 필수)
val policy = """
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": ["*"] },
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::$bucketName/*"]
}
]
}
""".trimIndent()
s3Client.putBucketPolicy {
it.bucket(bucketName).policy(policy)
}
}
}
}