diff --git a/src/main/java/org/fontory/fontorybe/file/adapter/inbound/S3UploadController.java b/src/main/java/org/fontory/fontorybe/file/adapter/inbound/S3UploadController.java index 77c8c9c..5eec382 100644 --- a/src/main/java/org/fontory/fontorybe/file/adapter/inbound/S3UploadController.java +++ b/src/main/java/org/fontory/fontorybe/file/adapter/inbound/S3UploadController.java @@ -1,5 +1,10 @@ package org.fontory.fontorybe.file.adapter.inbound; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.servlet.http.HttpServletRequest; import org.fontory.fontorybe.authentication.adapter.inbound.Login; import org.fontory.fontorybe.authentication.adapter.inbound.OAuth2; @@ -9,6 +14,7 @@ import org.fontory.fontorybe.file.domain.FileCreate; import org.fontory.fontorybe.file.domain.FileDetails; import org.fontory.fontorybe.provide.domain.Provide; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -20,6 +26,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import java.util.List; + import static org.fontory.fontorybe.file.validator.MultipartFileValidator.*; @Slf4j @@ -43,18 +51,30 @@ private void logFileDetails(MultipartFile file, String context) { file.getContentType()); } - @PostMapping("/profile-image") + + @Operation(summary = "프로필 이미지 업로드") + @PostMapping(value = "/profile-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity uploadMemberProfileImage( @OAuth2 Provide provide, - HttpServletRequest request -// @RequestPart MultipartFile file + @Parameter( + description = "업로드할 파일. 정확히 1개의 파일만 제공되어야 합니다.", + required = true, + content = @Content( + mediaType = MediaType.MULTIPART_FORM_DATA_VALUE, + array = @ArraySchema( + schema = @Schema(type = "string", format = "binary"), + maxItems = 1 + ) + ) + ) + @RequestPart("file") List files ) { - MultipartFile file = extractSingleMultipartFile(request); + MultipartFile file = extractSingleMultipartFile(files); - log.info("Request received: Upload new profile image for oauth provider: {}, provideId: {}", + log.info("Request received: Upload new profile image for oauth provider: {}, provideId: {}", provide.getProvider(), provide.getId()); logFileDetails(file, "New profile image upload"); - + FileCreate fileCreate = fileRequestMapper.toProfileImageFileCreate(file, provide); FileDetails fileDetails = fileService.uploadProfileImage(fileCreate); @@ -65,14 +85,25 @@ public ResponseEntity uploadMemberProfileImage( .body(FileUploadResponse.from(fileDetails)); } - @PutMapping("/profile-image") + @Operation(summary = "프로필 이미지 업데이트") + @PutMapping(value = "/profile-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity uploadMemberProfileImage( @Login UserPrincipal userPrincipal, - HttpServletRequest request -// @RequestPart MultipartFile file + @Parameter( + description = "업로드할 파일. 정확히 1개의 파일만 제공되어야 합니다.", + required = true, + content = @Content( + mediaType = MediaType.MULTIPART_FORM_DATA_VALUE, + array = @ArraySchema( + schema = @Schema(type = "string", format = "binary"), + maxItems = 1 + ) + ) + ) + @RequestPart("file") List files ) { Long memberId = userPrincipal.getId(); - MultipartFile file = extractSingleMultipartFile(request); + MultipartFile file = extractSingleMultipartFile(files); log.info("Request received: Update profile image for member ID: {}", memberId); logFileDetails(file, "Profile image update"); diff --git a/src/main/java/org/fontory/fontorybe/file/validator/MultipartFileValidator.java b/src/main/java/org/fontory/fontorybe/file/validator/MultipartFileValidator.java index c103406..8955992 100644 --- a/src/main/java/org/fontory/fontorybe/file/validator/MultipartFileValidator.java +++ b/src/main/java/org/fontory/fontorybe/file/validator/MultipartFileValidator.java @@ -1,35 +1,33 @@ package org.fontory.fontorybe.file.validator; -import jakarta.servlet.http.HttpServletRequest; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.fontory.fontorybe.file.domain.exception.SingleFileRequiredException; -import org.fontory.fontorybe.file.domain.exception.InvalidMultipartRequestException; import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; import java.util.List; @Slf4j @NoArgsConstructor(access = AccessLevel.PRIVATE) public class MultipartFileValidator { - public static MultipartFile extractSingleMultipartFile(HttpServletRequest request) { - if (!(request instanceof MultipartHttpServletRequest multipartRequest)) { - log.error("Invalid request: Not a multipart request."); - throw new InvalidMultipartRequestException(); + public static MultipartFile extractSingleMultipartFile(List files) { + if (files == null || files.isEmpty()) { + log.error("No file uploaded. Exactly one file must be provided."); + throw new SingleFileRequiredException(); } - - log.debug("Multipart request received."); - List files = multipartRequest.getFiles("file"); - log.info("Number of files received: {}", files.size()); - if (files.size() != 1) { log.error("Invalid file count: {}. Exactly one file must be uploaded.", files.size()); throw new SingleFileRequiredException(); } MultipartFile file = files.get(0); + + if (file.isEmpty()) { + log.error("Uploaded file is empty."); + throw new SingleFileRequiredException(); + } + log.info("Single file extracted successfully: filename={}, size={} bytes", file.getOriginalFilename(), file.getSize()); return file;