Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#36] 파일 벌크 업로드 API 추가 #37

Merged
merged 1 commit into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ class Constant {
const val MAX_IMAGE_FILE_SIZE = 10 * 1024 * 1024

val ALLOWED_IMAGE_FILE_EXTENSIONS = setOf("image/jpeg", "image/png")

val ALLOWED_AUDIO_FILE_EXTENSIONS = setOf("audio/mpeg")

val ALLOWED_FILE_EXTENSIONS = ALLOWED_AUDIO_FILE_EXTENSIONS + ALLOWED_IMAGE_FILE_EXTENSIONS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class Feedback(

val practiceId: Long,

@Column(columnDefinition = "smallint(5) unsigned")
@Column(columnDefinition = "smallint unsigned")
val score: Int
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(prefix = "file")
data class FileProperties(
val basePath: String = "",
val thumbnailPath: String = ""
val thumbnailPath: String = "",
val audioPath: String = "",
) {
val fullThumbnailPath: String
get() = "$basePath/$thumbnailPath"
.replace("//", "/")

val fullAudioPath: String
get() = "$basePath/$audioPath"
.replace("//", "/")
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,14 @@ class FileController(
return ResponseEntity.status(CREATED)
.body(FileResponse.from(fileEntity))
}

@PostMapping("/upload/bulk")
fun uploadBulkFile(
@RequestPart(name = "file") multipartFiles: List<MultipartFile>
): ResponseEntity<FileListResponse> {
val fileEntities = fileUploadService.uploadBulk(multipartFiles)

return ResponseEntity.status(CREATED)
.body(FileListResponse.from(fileEntities))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.kkeunkkeun.pregen.presentation.file.presentation

import org.kkeunkkeun.pregen.presentation.file.domain.File

data class FileListResponse(
val files: List<FileResponse>,
) {

companion object {
fun from(files: List<File>): FileListResponse {
val fileResponses = files.map { file -> FileResponse.from(file) }

return FileListResponse(fileResponses)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
package org.kkeunkkeun.pregen.presentation.file.service

import org.kkeunkkeun.pregen.presentation.file.domain.File
import org.kkeunkkeun.pregen.presentation.file.infrastructure.FileProperties
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.util.StringUtils
import org.springframework.web.multipart.MultipartFile
import java.util.UUID
import java.util.*

@Service
@Transactional
class FileUploadService(
private val validator: FileValidator,
private val fileRepository: FileRepository,
private val fileWriter: FileWriter,
private val fileProperties: FileProperties,
) {

fun upload(file: MultipartFile): File {
validator.validate(file)

return saveAndWriteFile(file)
}

fun uploadBulk(files: List<MultipartFile>): List<File> {
files.forEach { file -> validator.validate(file) }

return files.map { file -> this.saveAndWriteFile(file) }
}

private fun saveAndWriteFile(file: MultipartFile): File {
val fileEntity = fileWriter.writeMultipartFile(
file = file,
path = fileProperties.fullThumbnailPath,
path = validator.detectPath(file),
physicalName = generateFilePhysicalName(file)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package org.kkeunkkeun.pregen.presentation.file.service

import org.kkeunkkeun.pregen.common.domain.Constant.Companion.ALLOWED_AUDIO_FILE_EXTENSIONS
import org.kkeunkkeun.pregen.common.domain.Constant.Companion.ALLOWED_FILE_EXTENSIONS
import org.kkeunkkeun.pregen.common.domain.Constant.Companion.ALLOWED_IMAGE_FILE_EXTENSIONS
import org.kkeunkkeun.pregen.common.domain.Constant.Companion.MAX_IMAGE_FILE_SIZE
import org.kkeunkkeun.pregen.presentation.file.infrastructure.FileProperties
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile

@Service
class FileValidator {
class FileValidator(
private val fileProperties: FileProperties,
) {

fun validate(file: MultipartFile) {
if (file.isEmpty) {
Expand All @@ -20,8 +25,20 @@ class FileValidator {
}

// Check the file type
if (!ALLOWED_IMAGE_FILE_EXTENSIONS.contains(file.contentType)) {
throw IllegalStateException("Invalid file type. Only JPG and PNG files are allowed.")
if (!ALLOWED_FILE_EXTENSIONS.contains(file.contentType)) {
throw IllegalStateException("Invalid file type.")
}
}

fun detectPath(file: MultipartFile): String {
if (ALLOWED_IMAGE_FILE_EXTENSIONS.contains(file.contentType)) {
return fileProperties.fullThumbnailPath
}

if (ALLOWED_AUDIO_FILE_EXTENSIONS.contains(file.contentType)) {
return fileProperties.fullAudioPath
}

throw IllegalStateException("Invalid file type.")
}
}
3 changes: 2 additions & 1 deletion presentation-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ spring:
password: admin
jpa:
hibernate:
ddl-auto: create
ddl-auto: validate
properties:
show_sql: false
hibernate:
Expand All @@ -38,3 +38,4 @@ spring:
file:
base-path: ./data
thumbnail-path: /thumbnails
audio-path: /audios