Skip to content

Commit

Permalink
ResourceType(rooms, users), ResourceId(roomId, userId) 조회 요청에 따른 이미지 …
Browse files Browse the repository at this point in the history
…업로드 URL, 이미지 다운로드 URL 제공하는 Service, Controller 수정 + Entity에 다운로드 및 수정 위한 조회 URL 추가
  • Loading branch information
JunBye committed Jan 14, 2025
1 parent a0f0b96 commit 09b6b27
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 47 deletions.
53 changes: 17 additions & 36 deletions src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageController.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.example.toyTeam6Airbnb.Image

import io.swagger.v3.oas.annotations.Operation
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@RestController
Expand All @@ -17,44 +16,26 @@ class ImageController(
summary = "Generate a presigned URL for uploading an image",
description = "Generates a presigned URL for uploading an image directly to S3."
)
@GetMapping("/upload-url")
fun generateUploadUrl(
@RequestParam("key") key: String,
@RequestParam("expirationMinutes", defaultValue = "10") expirationMinutes: Long
): ResponseEntity<String> {
return try {
val uploadUrl = imageService.generateUploadUrl(key, expirationMinutes)
ResponseEntity.ok(uploadUrl)
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to generate upload URL: ${e.message}")
}
// 업로드 URL 생성
@PostMapping("/upload")
fun generateUploadUrl(@RequestBody request: ImageRequest): ResponseEntity<Map<String, String>> {
val uploadUrl = imageService.generateUploadUrl(request.resourceType, request.resourceId)
return ResponseEntity.ok(mapOf("uploadUrl" to uploadUrl))
}

@Operation(
summary = "Generate a presigned URL for downloading an image",
description = "Generates a presigned URL for downloading an image directly from S3."
)
@GetMapping("/download-url")
fun generateDownloadUrl(
@RequestParam("key") key: String,
@RequestParam("expirationMinutes", defaultValue = "60") expirationMinutes: Long
): ResponseEntity<String> {
return try {
val downloadUrl = imageService.generateDownloadUrl(key, expirationMinutes)
ResponseEntity.ok(downloadUrl)
} catch (e: Exception) {
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to generate download URL: ${e.message}")
}
// 다운로드 URL 생성
@PostMapping("/download")
fun generateDownloadUrl(@RequestBody request: ImageRequest): ResponseEntity<Map<String, String>> {
val downloadUrl = imageService.generateDownloadUrl(request.resourceType, request.resourceId)
return ResponseEntity.ok(mapOf("downloadUrl" to downloadUrl))
}

// // 내부 DTO 클래스 정의
// data class UploadImageRequest(
// @Schema(type = "string", format = "binary", description = "The image file to upload")
// val file: MultipartFile,
//
// @Schema(description = "The key to associate with the uploaded image", example = "example-key")
// val key: String
// )
}

data class ImageRequest(
val resourceType: String,
val resourceId: String
)
62 changes: 52 additions & 10 deletions src/main/kotlin/com/example/toyTeam6Airbnb/Image/ImageService.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.example.toyTeam6Airbnb.Image

import com.example.toyTeam6Airbnb.room.persistence.RoomRepository
import com.example.toyTeam6Airbnb.user.persistence.UserRepository
import org.springdoc.webmvc.ui.SwaggerResourceResolver
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import software.amazon.awssdk.regions.Region
Expand All @@ -11,9 +15,14 @@ import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignReques
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest
import java.time.Duration
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap

@Service
class ImageService() {
class ImageService(
@Autowired private val userRepository: UserRepository,
@Autowired private val roomRepository: RoomRepository,
private val swaggerResourceResolver: SwaggerResourceResolver
) {

@Value("\${cloudfront.private-key}")
private lateinit var privateKey: String
Expand All @@ -31,37 +40,70 @@ class ImageService() {

private val bucketName: String = "waffle-team6-storage"
private val cloudFrontUrl: String = "https://d3m9s5wmwvsq01.cloudfront.net"
private val filePathMap: MutableMap<String, String> = ConcurrentHashMap() // 리소스 타입과 ID로 파일 경로 매핑

// Presigned URL for Upload
fun generateUploadUrl(key: String, expirationMinutes: Long): String {
val filePath = "$key/${UUID.randomUUID()}_upload.jpg" // 고유 파일 경로 생성
fun generateUploadUrl(resourceType: String, resourceId: String): String {
val filePath = "$resourceType/$resourceId/${UUID.randomUUID()}_image.jpg"
filePathMap["$resourceType:$resourceId"] = filePath // 리소스별 경로 매핑

val putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(filePath)
.build()

val presignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(expirationMinutes))
.signatureDuration(Duration.ofMinutes(60))
.putObjectRequest(putObjectRequest)
.build()

return s3Presigner.presignPutObject(presignRequest).url().toString()
val uploadUrl = s3Presigner.presignPutObject(presignRequest).url().toString()

if (resourceType == "users") {
val userId = resourceId.toLongOrNull() ?: throw RuntimeException("Invalid user ID")
val user = userRepository.findById(userId).orElseThrow { RuntimeException("User not found") }
user.imageDownloadUrl = uploadUrl
userRepository.save(user)
} else if (resourceType == "rooms") {
val roomId = resourceId.toLongOrNull() ?: throw RuntimeException("Invalid room ID")
val room = roomRepository.findById(roomId).orElseThrow { RuntimeException("Room not found") }
room.imageDownloadUrl = uploadUrl
roomRepository.save(room)
}

return uploadUrl
}

// Presigned URL for Download
// 사용자가 Upload시 생성한 Key를 그대로 입력하면 됨. (UUID 없이)
fun generateDownloadUrl(key: String, expirationMinutes: Long): String {
fun generateDownloadUrl(resourceType: String, resourceId: String): String {
val filePath = filePathMap["$resourceType:$resourceId"]
?: throw IllegalArgumentException("No file found for the given resource: $resourceType with ID: $resourceId")

val getObjectRequest = GetObjectRequest.builder()
.bucket(bucketName)
.key(key)
.key(filePath)
.build()

val presignRequest = GetObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(expirationMinutes))
.signatureDuration(Duration.ofMinutes(60))
.getObjectRequest(getObjectRequest)
.build()

return s3Presigner.presignGetObject(presignRequest).url().toString()
val downloadUrl = s3Presigner.presignGetObject(presignRequest).url().toString()

if (resourceType == "users") {
val userId = resourceId.toLongOrNull() ?: throw RuntimeException("Invalid user ID")
val user = userRepository.findById(userId).orElseThrow { RuntimeException("User not found") }
user.imageDownloadUrl = downloadUrl
userRepository.save(user)
} else if (resourceType == "rooms") {
val roomId = resourceId.toLongOrNull() ?: throw RuntimeException("Invalid room ID")
val room = roomRepository.findById(roomId).orElseThrow { RuntimeException("Room not found") }
room.imageDownloadUrl = downloadUrl
roomRepository.save(room)
}

return downloadUrl
}

// // 파일 업로드 메서드
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ class RoomEntity(
@Column(nullable = false)
var createdAt: Instant = Instant.now(),
@Column(nullable = false)
var updatedAt: Instant = Instant.now()
var updatedAt: Instant = Instant.now(),
@Column
var imageDownloadUrl: String? = null,
@Column
var imageUploadUrl: String? = null
) {
@PrePersist
fun onPrePersist() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class UserEntity(
val provider: AuthProvider,
@Column
var oAuthId: String? = null,
@Column
var imageDownloadUrl: String? = null,
@Column
var imageUploadUrl: String? = null,

@OneToMany(mappedBy = "user", cascade = [CascadeType.ALL], orphanRemoval = true)
val reservations: List<ReservationEntity> = mutableListOf(),
Expand Down

0 comments on commit 09b6b27

Please sign in to comment.