mirror of https://github.com/usbharu/Hideout.git
feat: S3以外にもメディアを保存できるように
This commit is contained in:
parent
4943b8e94e
commit
a8da0f5366
|
@ -10,7 +10,7 @@ import java.net.URI
|
||||||
@Configuration
|
@Configuration
|
||||||
class AwsConfig {
|
class AwsConfig {
|
||||||
@Bean
|
@Bean
|
||||||
fun s3Client(awsConfig: StorageConfig): S3Client {
|
fun s3Client(awsConfig: S3StorageConfig): S3Client {
|
||||||
return S3Client.builder()
|
return S3Client.builder()
|
||||||
.endpointOverride(URI.create(awsConfig.endpoint))
|
.endpointOverride(URI.create(awsConfig.endpoint))
|
||||||
.region(Region.of(awsConfig.region))
|
.region(Region.of(awsConfig.region))
|
||||||
|
|
|
@ -14,7 +14,7 @@ class SpringConfig {
|
||||||
lateinit var config: ApplicationConfig
|
lateinit var config: ApplicationConfig
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
lateinit var storageConfig: StorageConfig
|
lateinit var s3StorageConfig: S3StorageConfig
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun requestLoggingFilter(): CommonsRequestLoggingFilter {
|
fun requestLoggingFilter(): CommonsRequestLoggingFilter {
|
||||||
|
@ -33,9 +33,8 @@ data class ApplicationConfig(
|
||||||
val url: URL
|
val url: URL
|
||||||
)
|
)
|
||||||
|
|
||||||
@ConfigurationProperties("hideout.storage")
|
@ConfigurationProperties("hideout.storage.s3")
|
||||||
data class StorageConfig(
|
data class S3StorageConfig(
|
||||||
val useS3: Boolean,
|
|
||||||
val endpoint: String,
|
val endpoint: String,
|
||||||
val publicUrl: String,
|
val publicUrl: String,
|
||||||
val bucket: String,
|
val bucket: String,
|
||||||
|
@ -44,6 +43,12 @@ data class StorageConfig(
|
||||||
val secretKey: String
|
val secretKey: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ConfigurationProperties("hideout.storage.local")
|
||||||
|
data class LocalStorageConfig(
|
||||||
|
val path: String,
|
||||||
|
val publicUrl: String?
|
||||||
|
)
|
||||||
|
|
||||||
@ConfigurationProperties("hideout.character-limit")
|
@ConfigurationProperties("hideout.character-limit")
|
||||||
data class CharacterLimit(
|
data class CharacterLimit(
|
||||||
val general: General = General(),
|
val general: General = General(),
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package dev.usbharu.hideout.core.service.media
|
||||||
|
|
||||||
|
import dev.usbharu.hideout.application.config.ApplicationConfig
|
||||||
|
import dev.usbharu.hideout.application.config.LocalStorageConfig
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.copyTo
|
||||||
|
import kotlin.io.path.deleteIfExists
|
||||||
|
import kotlin.io.path.outputStream
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class LocalFileSystemMediaDataStore(
|
||||||
|
applicationConfig: ApplicationConfig,
|
||||||
|
localStorageConfig: LocalStorageConfig
|
||||||
|
) : MediaDataStore {
|
||||||
|
|
||||||
|
private val savePath: Path = Path.of(localStorageConfig.path)
|
||||||
|
|
||||||
|
private val publicUrl = localStorageConfig.publicUrl ?: "${applicationConfig.url}/files/"
|
||||||
|
|
||||||
|
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
||||||
|
val fileSavePath = buildSavePath(savePath, dataMediaSave.name)
|
||||||
|
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataMediaSave.name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dataMediaSave.thumbnailInputStream?.inputStream()?.buffered()
|
||||||
|
?.transferTo(thumbnailSavePath.outputStream().buffered())
|
||||||
|
dataMediaSave.fileInputStream.inputStream().buffered().transferTo(fileSavePath.outputStream().buffered())
|
||||||
|
|
||||||
|
return SuccessSavedMedia(
|
||||||
|
dataMediaSave.name,
|
||||||
|
publicUrl + dataMediaSave.name,
|
||||||
|
publicUrl + "thumbnail-" + dataMediaSave.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun save(dataSaveRequest: MediaSaveRequest): SavedMedia {
|
||||||
|
val fileSavePath = buildSavePath(savePath, dataSaveRequest.name)
|
||||||
|
val thumbnailSavePath = buildSavePath(savePath, "thumbnail-" + dataSaveRequest.name)
|
||||||
|
|
||||||
|
dataSaveRequest.filePath.copyTo(fileSavePath)
|
||||||
|
dataSaveRequest.thumbnailPath?.copyTo(thumbnailSavePath)
|
||||||
|
|
||||||
|
return SuccessSavedMedia(
|
||||||
|
dataSaveRequest.name,
|
||||||
|
publicUrl + dataSaveRequest.name,
|
||||||
|
publicUrl + "thumbnail-" + dataSaveRequest.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun delete(id: String) {
|
||||||
|
buildSavePath(savePath, id).deleteIfExists()
|
||||||
|
buildSavePath(savePath, "thumbnail-$id").deleteIfExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildSavePath(savePath: Path, name: String): Path = savePath.resolve(name)
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package dev.usbharu.hideout.core.service.media
|
package dev.usbharu.hideout.core.service.media
|
||||||
|
|
||||||
import dev.usbharu.hideout.application.config.StorageConfig
|
import dev.usbharu.hideout.application.config.S3StorageConfig
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
@ -14,16 +14,16 @@ import software.amazon.awssdk.services.s3.model.GetUrlRequest
|
||||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest
|
import software.amazon.awssdk.services.s3.model.PutObjectRequest
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig: StorageConfig) : MediaDataStore {
|
class S3MediaDataStore(private val s3Client: S3Client, private val s3StorageConfig: S3StorageConfig) : MediaDataStore {
|
||||||
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
override suspend fun save(dataMediaSave: MediaSave): SavedMedia {
|
||||||
val fileUploadRequest = PutObjectRequest.builder()
|
val fileUploadRequest = PutObjectRequest.builder()
|
||||||
.bucket(storageConfig.bucket)
|
.bucket(s3StorageConfig.bucket)
|
||||||
.key(dataMediaSave.name)
|
.key(dataMediaSave.name)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val thumbnailKey = "thumbnail-${dataMediaSave.name}"
|
val thumbnailKey = "thumbnail-${dataMediaSave.name}"
|
||||||
val thumbnailUploadRequest = PutObjectRequest.builder()
|
val thumbnailUploadRequest = PutObjectRequest.builder()
|
||||||
.bucket(storageConfig.bucket)
|
.bucket(s3StorageConfig.bucket)
|
||||||
.key(thumbnailKey)
|
.key(thumbnailKey)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig
|
||||||
RequestBody.fromBytes(dataMediaSave.thumbnailInputStream)
|
RequestBody.fromBytes(dataMediaSave.thumbnailInputStream)
|
||||||
)
|
)
|
||||||
s3Client.utilities()
|
s3Client.utilities()
|
||||||
.getUrl(GetUrlRequest.builder().bucket(storageConfig.bucket).key(thumbnailKey).build())
|
.getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(thumbnailKey).build())
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -44,14 +44,14 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig
|
||||||
async {
|
async {
|
||||||
s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream))
|
s3Client.putObject(fileUploadRequest, RequestBody.fromBytes(dataMediaSave.fileInputStream))
|
||||||
s3Client.utilities()
|
s3Client.utilities()
|
||||||
.getUrl(GetUrlRequest.builder().bucket(storageConfig.bucket).key(dataMediaSave.name).build())
|
.getUrl(GetUrlRequest.builder().bucket(s3StorageConfig.bucket).key(dataMediaSave.name).build())
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return SuccessSavedMedia(
|
return SuccessSavedMedia(
|
||||||
name = dataMediaSave.name,
|
name = dataMediaSave.name,
|
||||||
url = "${storageConfig.publicUrl}/${storageConfig.bucket}/${dataMediaSave.name}",
|
url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataMediaSave.name}",
|
||||||
thumbnailUrl = "${storageConfig.publicUrl}/${storageConfig.bucket}/$thumbnailKey"
|
thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,19 +59,19 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig
|
||||||
logger.info("MEDIA upload. {}", dataSaveRequest.name)
|
logger.info("MEDIA upload. {}", dataSaveRequest.name)
|
||||||
|
|
||||||
val fileUploadRequest = PutObjectRequest.builder()
|
val fileUploadRequest = PutObjectRequest.builder()
|
||||||
.bucket(storageConfig.bucket)
|
.bucket(s3StorageConfig.bucket)
|
||||||
.key(dataSaveRequest.name)
|
.key(dataSaveRequest.name)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
logger.info("MEDIA upload. bucket: {} key: {}", storageConfig.bucket, dataSaveRequest.name)
|
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, dataSaveRequest.name)
|
||||||
|
|
||||||
val thumbnailKey = "thumbnail-${dataSaveRequest.name}"
|
val thumbnailKey = "thumbnail-${dataSaveRequest.name}"
|
||||||
val thumbnailUploadRequest = PutObjectRequest.builder()
|
val thumbnailUploadRequest = PutObjectRequest.builder()
|
||||||
.bucket(storageConfig.bucket)
|
.bucket(s3StorageConfig.bucket)
|
||||||
.key(thumbnailKey)
|
.key(thumbnailKey)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
logger.info("MEDIA upload. bucket: {} key: {}", storageConfig.bucket, thumbnailKey)
|
logger.info("MEDIA upload. bucket: {} key: {}", s3StorageConfig.bucket, thumbnailKey)
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
awaitAll(
|
awaitAll(
|
||||||
|
@ -92,8 +92,8 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig
|
||||||
}
|
}
|
||||||
val successSavedMedia = SuccessSavedMedia(
|
val successSavedMedia = SuccessSavedMedia(
|
||||||
name = dataSaveRequest.name,
|
name = dataSaveRequest.name,
|
||||||
url = "${storageConfig.publicUrl}/${storageConfig.bucket}/${dataSaveRequest.name}",
|
url = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/${dataSaveRequest.name}",
|
||||||
thumbnailUrl = "${storageConfig.publicUrl}/${storageConfig.bucket}/$thumbnailKey"
|
thumbnailUrl = "${s3StorageConfig.publicUrl}/${s3StorageConfig.bucket}/$thumbnailKey"
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info("SUCCESS Media upload. {}", dataSaveRequest.name)
|
logger.info("SUCCESS Media upload. {}", dataSaveRequest.name)
|
||||||
|
@ -108,9 +108,9 @@ class S3MediaDataStore(private val s3Client: S3Client, private val storageConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(id: String) {
|
override suspend fun delete(id: String) {
|
||||||
val fileDeleteRequest = DeleteObjectRequest.builder().bucket(storageConfig.bucket).key(id).build()
|
val fileDeleteRequest = DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key(id).build()
|
||||||
val thumbnailDeleteRequest =
|
val thumbnailDeleteRequest =
|
||||||
DeleteObjectRequest.builder().bucket(storageConfig.bucket).key("thumbnail-$id").build()
|
DeleteObjectRequest.builder().bucket(s3StorageConfig.bucket).key("thumbnail-$id").build()
|
||||||
s3Client.deleteObject(fileDeleteRequest)
|
s3Client.deleteObject(fileDeleteRequest)
|
||||||
s3Client.deleteObject(thumbnailDeleteRequest)
|
s3Client.deleteObject(thumbnailDeleteRequest)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue