feat: S3アップロードを実装

This commit is contained in:
usbharu 2023-10-05 00:16:56 +09:00
parent 0fd620d16e
commit 60242693f9
12 changed files with 97 additions and 33 deletions

View File

@ -119,6 +119,8 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-mongodb") implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.jetbrains.exposed:exposed-spring-boot-starter:0.44.0") implementation("org.jetbrains.exposed:exposed-spring-boot-starter:0.44.0")
implementation("io.trbl:blurhash:1.0.0") implementation("io.trbl:blurhash:1.0.0")
implementation("software.amazon.awssdk:s3:2.20.157")
implementation("io.ktor:ktor-client-logging-jvm:$ktor_version") implementation("io.ktor:ktor-client-logging-jvm:$ktor_version")

View File

@ -0,0 +1,16 @@
package dev.usbharu.hideout.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.services.s3.S3Client
@Configuration
class AwsConfig {
@Bean
fun s3(awsConfig: StorageConfig): S3Client {
return S3Client.builder()
.credentialsProvider { AwsBasicCredentials.create(awsConfig.accessKey, awsConfig.secretKey) }
.build()
}
}

View File

@ -1,10 +1,8 @@
package dev.usbharu.hideout.domain.model package dev.usbharu.hideout.domain.model
import java.io.OutputStream
data class MediaSave( data class MediaSave(
val name: String, val name: String,
val prefix: String, val prefix: String,
val fileInputStream: OutputStream, val fileInputStream: ByteArray,
val thumbnailInputStream: OutputStream? val thumbnailInputStream: ByteArray?
) )

View File

@ -6,7 +6,6 @@ class SuccessSavedMedia(
val name: String, val name: String,
val url: String, val url: String,
val thumbnailUrl: String, val thumbnailUrl: String,
val blurhash: String
) : ) :
SavedMedia(true) SavedMedia(true)

View File

@ -37,7 +37,7 @@ class MediaServiceImpl(
throw UnsupportedMediaException("FileType: $fileType is not supported.") throw UnsupportedMediaException("FileType: $fileType is not supported.")
} }
val process = mediaProcessService.process(fileType, media.file.inputStream, media.thumbnail?.inputStream) val process = mediaProcessService.process(fileType, media.file.bytes, media.thumbnail?.bytes)
val dataMediaSave = MediaSave( val dataMediaSave = MediaSave(
UUID.randomUUID().toString(), UUID.randomUUID().toString(),

View File

@ -0,0 +1,59 @@
package dev.usbharu.hideout.service.media
import dev.usbharu.hideout.config.StorageConfig
import dev.usbharu.hideout.domain.model.MediaSave
import dev.usbharu.hideout.domain.model.hideout.dto.SavedMedia
import dev.usbharu.hideout.domain.model.hideout.dto.SuccessSavedMedia
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import org.springframework.stereotype.Service
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
@Service
class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig: StorageConfig) : MediaDataStore {
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
val fileUploadRequest = PutObjectRequest.builder()
.bucket(storageConfig.bucket)
.key(dataMediaSave.name)
.build()
val thumbnailKey = "thumbnail-${dataMediaSave.name}"
val thumbnailUploadRequest = PutObjectRequest.builder()
.bucket(storageConfig.bucket)
.key(thumbnailKey)
.build()
val pairList = withContext(Dispatchers.IO) {
awaitAll(
async {
if (dataMediaSave.thumbnailInputStream != null) {
s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.thumbnailInputStream))
"thumbnail" to s3Client.utilities()
.getUrl(GetUrlRequest.builder().bucket(storageConfig.bucket).key(thumbnailKey).build())
} else {
"thumbnail" to null
}
},
async {
s3Client.putObject(thumbnailUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream))
"file" to s3Client.utilities()
.getUrl(GetUrlRequest.builder().bucket(storageConfig.bucket).key(dataMediaSave.name).build())
}
)
}.toMap()
return SuccessSavedMedia(
dataMediaSave.name,
pairList.getValue("file").toString(),
pairList.getValue("thumbnail").toString()
)
}
override suspend fun delete(id: Long) {
TODO("Not yet implemented")
}
}

View File

@ -1,10 +1,8 @@
package dev.usbharu.hideout.service.media package dev.usbharu.hideout.service.media
import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
interface ThumbnailGenerateService { interface ThumbnailGenerateService {
fun generate(bufferedImage: InputStream, width: Int, height: Int): ByteArrayOutputStream fun generate(bufferedImage: InputStream, width: Int, height: Int): ByteArray
fun generate(outputStream: OutputStream, width: Int, height: Int): ByteArrayOutputStream fun generate(outputStream: ByteArray, width: Int, height: Int): ByteArray
} }

View File

@ -2,9 +2,8 @@ package dev.usbharu.hideout.service.media.converter
import dev.usbharu.hideout.domain.model.hideout.dto.FileType import dev.usbharu.hideout.domain.model.hideout.dto.FileType
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
interface MediaConverter { interface MediaConverter {
fun isSupport(fileType: FileType): Boolean fun isSupport(fileType: FileType): Boolean
fun convert(inputStream: InputStream): OutputStream fun convert(inputStream: InputStream): ByteArray
} }

View File

@ -2,8 +2,7 @@ package dev.usbharu.hideout.service.media.converter
import dev.usbharu.hideout.domain.model.hideout.dto.FileType import dev.usbharu.hideout.domain.model.hideout.dto.FileType
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
interface MediaConverterRoot { interface MediaConverterRoot {
suspend fun convert(fileType: FileType, inputStream: InputStream): OutputStream suspend fun convert(fileType: FileType, inputStream: InputStream): ByteArray
} }

View File

@ -1,20 +1,18 @@
package dev.usbharu.hideout.service.media.converter package dev.usbharu.hideout.service.media.converter
import dev.usbharu.hideout.domain.model.hideout.dto.FileType import dev.usbharu.hideout.domain.model.hideout.dto.FileType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
@Service @Service
class MediaConverterRootImpl(private val converters: List<MediaConverter>) : MediaConverterRoot { class MediaConverterRootImpl(private val converters: List<MediaConverter>) : MediaConverterRoot {
override suspend fun convert(fileType: FileType, inputStream: InputStream): OutputStream { override suspend fun convert(fileType: FileType, inputStream: InputStream): ByteArray {
return converters.find { return converters.find {
it.isSupport(fileType) it.isSupport(fileType)
}?.convert(inputStream) ?: inputStream.let { }?.convert(inputStream) ?: withContext(Dispatchers.IO) {
val byteArrayOutputStream = ByteArrayOutputStream() inputStream.readAllBytes()
it.transferTo(byteArrayOutputStream)
byteArrayOutputStream
} }
} }
} }

View File

@ -1,13 +1,11 @@
package dev.usbharu.hideout.service.media.converter package dev.usbharu.hideout.service.media.converter
import dev.usbharu.hideout.domain.model.hideout.dto.FileType import dev.usbharu.hideout.domain.model.hideout.dto.FileType
import java.io.InputStream
import java.io.OutputStream
interface MediaProcessService { interface MediaProcessService {
suspend fun process( suspend fun process(
fileType: FileType, fileType: FileType,
file: InputStream, file: ByteArray,
thumbnail: InputStream? thumbnail: ByteArray?
): Pair<OutputStream, OutputStream> ): Pair<ByteArray, ByteArray>
} }

View File

@ -5,8 +5,6 @@ import dev.usbharu.hideout.exception.media.MediaConvertException
import dev.usbharu.hideout.service.media.ThumbnailGenerateService import dev.usbharu.hideout.service.media.ThumbnailGenerateService
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.InputStream
import java.io.OutputStream
@Service @Service
class MediaProcessServiceImpl( class MediaProcessServiceImpl(
@ -15,18 +13,18 @@ class MediaProcessServiceImpl(
) : MediaProcessService { ) : MediaProcessService {
override suspend fun process( override suspend fun process(
fileType: FileType, fileType: FileType,
file: InputStream, file: ByteArray,
thumbnail: InputStream? thumbnail: ByteArray?
): Pair<OutputStream, OutputStream> { ): Pair<ByteArray, ByteArray> {
val fileInputStream = try { val fileInputStream = try {
mediaConverterRoot.convert(fileType, file) mediaConverterRoot.convert(fileType, file.inputStream().buffered())
} catch (e: Exception) { } catch (e: Exception) {
logger.warn("Failed convert media.", e) logger.warn("Failed convert media.", e)
throw MediaConvertException("Failed convert media.", e) throw MediaConvertException("Failed convert media.", e)
} }
val thumbnailInputStream = try { val thumbnailInputStream = try {
thumbnail?.let { mediaConverterRoot.convert(fileType, it) } thumbnail?.let { mediaConverterRoot.convert(fileType, it.inputStream().buffered()) }
} catch (e: Exception) { } catch (e: Exception) {
logger.warn("Failed convert thumbnail media.", e) logger.warn("Failed convert thumbnail media.", e)
null null