diff --git a/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageController.kt b/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageController.kt index 0e912ec..a39ffe6 100644 --- a/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageController.kt +++ b/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageController.kt @@ -12,8 +12,6 @@ import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import org.springframework.web.multipart.MultipartFile -import java.nio.file.Paths -import java.util.Date @RestController @RequestMapping("/api/images") @@ -40,32 +38,18 @@ class ImageController( return try { val file = request.file val key = request.key.trim() - - val tempFile = Paths.get(System.getProperty("java.io.tmpdir"), file.originalFilename).toFile() - file.transferTo(tempFile) // MultipartFile을 임시 파일로 저장 - - imageService.uploadFile(tempFile.absolutePath, key) // S3에 업로드 - tempFile.delete() // 임시 파일 삭제 - - ResponseEntity("Image uploaded successfully with key: $key", HttpStatus.OK) + val fileUrl = imageService.uploadFile(file, key) // S3에 업로드 및 경로 저장 + ResponseEntity.ok("Image uploaded successfully with key: $key, URL: $fileUrl") } catch (e: Exception) { - ResponseEntity("Failed to upload image: ${e.message}", HttpStatus.INTERNAL_SERVER_ERROR) + ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("Failed to upload image: ${e.message}") } } - @GetMapping("/{key}") - fun getImageSignedUrl( - @PathVariable("key") key: String - ): ResponseEntity { + @GetMapping("/get-signed-url/{key}") + fun getSignedUrl(@PathVariable key: String): ResponseEntity { return try { - val expirationDate = Date(System.currentTimeMillis() + 3600_000) // 1시간 유효 - - val signedUrl = imageService.generateSignedUrl( - "https://d3m9s5wmwvsq01.cloudfront.net", - key, - expirationDate - ) - + val signedUrl = imageService.generateSignedUrl(key) ResponseEntity.ok(signedUrl) } catch (e: Exception) { ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) diff --git a/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageService.kt b/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageService.kt index b26daae..e48b94c 100644 --- a/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageService.kt +++ b/src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageService.kt @@ -4,14 +4,17 @@ import com.amazonaws.services.cloudfront.CloudFrontUrlSigner import com.amazonaws.services.cloudfront.util.SignerUtils 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.regions.Region import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.PutObjectRequest import java.io.File +import java.io.IOException import java.nio.file.Paths import java.security.PrivateKey import java.util.Date +import java.util.UUID @Service class ImageService() { @@ -28,44 +31,77 @@ class ImageService() { private val cloudFrontUrl: String = "https://d3m9s5wmwvsq01.cloudfront.net" // 파일 업로드 메서드 - fun uploadFile(filePath: String, key: String) { - println("Uploading File with key : $key") // 디버깅 - s3Client.putObject( - PutObjectRequest.builder() - .bucket(bucketName) - .key(key) - .build(), - RequestBody.fromFile(Paths.get(filePath)) - ) - } + fun uploadFile(file: MultipartFile, key: String): String { + val fileName = "$key/${UUID.randomUUID()}_${file.originalFilename}" // 고유 경로 생성 + try { + val tempFile = Paths.get(System.getProperty("java.io.tmpdir"), file.originalFilename).toFile() + file.transferTo(tempFile) -// // 파일 다운로드 메서드 -// @Transactional -// fun downloadFile(key: String, destination: String) { -// s3Client.getObject( -// GetObjectRequest.builder() -// .bucket(bucketName) -// .key(key) -// .build(), -// Paths.get(destination) -// ) -// } + s3Client.putObject( + PutObjectRequest.builder() + .bucket(bucketName) + .key(fileName) + .build(), + RequestBody.fromFile(tempFile.toPath()) + ) + tempFile.delete() - // CloudFront signed URL 생성 메서드 - fun generateSignedUrl( - domain: String, - key: String, - expiration: Date - ): String { - // privateKey와 keyPairId는 GitHub Secrets에서 가져온 값이 자동으로 주입됨 - if (privateKey.isBlank()) { - throw IllegalStateException("Private key is not configured or is blank") + // DB에 파일 경로 저장 saveFilePathToDatabase(key, fileName) (추후 Room, User 엔티티에 추가식으로 수정) + // ProfileImageUpload, RoomImageUplaod 형식으로 할것 + } catch (e: IOException) { + throw RuntimeException("S3 업로드 중 에러가 발생했습니다: ${e.message}", e) } - if (keyPairId.isBlank()) { - throw IllegalStateException("Key Pair ID is not configured or is blank") + + return "https://$bucketName.s3.amazonaws.com/$fileName" + } + + // CloudFront signed URL 생성 메서드 + fun generateSignedUrl(key: String): String { + try { + if (privateKey.isBlank()) { + throw IllegalStateException("Private key is not configured or is blank") + } + if (keyPairId.isBlank()) { + throw IllegalStateException("Key Pair ID is not configured or is blank") + } + // S3 객체 경로 생성 (key 폴더 사용) + val filePath = "$key/" + + // 만료 시간 설정 (예: 1시간 후) + val expirationDate = Date(System.currentTimeMillis() + 3600_000) + + // CloudFront 서명된 URL 생성 + // 1. 임시 파일 생성 및 비공개 키 쓰기 + val tempFile = File.createTempFile("privateKey", ".pem") + tempFile.deleteOnExit() // 애플리케이션 종료 시 자동 삭제 + tempFile.writeText(privateKey) + + // 2. Private Key 객체 로드 + val privateKeyObj: PrivateKey = SignerUtils.loadPrivateKey(tempFile) + return CloudFrontUrlSigner.getSignedURLWithCannedPolicy( + "https://d3m9s5wmwvsq01.cloudfront.net/$filePath", + keyPairId, // Key Pair ID + privateKeyObj, + expirationDate + ) + } catch (e: Exception) { + throw RuntimeException("Signed URL 생성 중 에러 발생: ${e.message}", e) } - return createSignedUrl(domain, key, privateKey, keyPairId, expiration) } +// fun generateSignedUrl( +// domain: String, +// key: String, +// expiration: Date +// ): String { +// // privateKey와 keyPairId는 GitHub Secrets에서 가져온 값이 자동으로 주입됨 +// if (privateKey.isBlank()) { +// throw IllegalStateException("Private key is not configured or is blank") +// } +// if (keyPairId.isBlank()) { +// throw IllegalStateException("Key Pair ID is not configured or is blank") +// } +// return createSignedUrl(domain, key, privateKey, keyPairId, expiration) +// } // CloudFront 서명 URL 생성 메서드 // createSignedUrl 메서드 구현