Skip to content

Commit

Permalink
feat: Add support for multiple file uploads
Browse files Browse the repository at this point in the history
Updated FileService to handle uploading of multiple files at once. Made corresponding changes to FileController and FileControllerTest to reflect this addition. Documentation updated to include the process for uploading multiple files.
  • Loading branch information
dygma0 committed Dec 23, 2023
1 parent be01d48 commit 755c889
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 13 deletions.
8 changes: 8 additions & 0 deletions perfume-api/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ include::{snippets}/file-upload/http-request.adoc[]
include::{snippets}/file-upload/http-response.adoc[]
include::{snippets}/file-upload/response-fields.adoc[]

== 파일 목록 업로드
=== 요청
include::{snippets}/files-upload/http-request.adoc[]

=== 응답
include::{snippets}/files-upload/http-response.adoc[]
include::{snippets}/files-upload/response-fields.adoc[]

== 알림 구독
=== 요청
include::{snippets}/connect-sse/http-request.adoc[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import io.perfume.api.file.application.service.FileService;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -16,24 +18,51 @@
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/v1/files")
@RequestMapping
@PreAuthorize("isAuthenticated()")
@RequiredArgsConstructor
public class FileController {

private final FileService fileService;

@PostMapping
public ResponseEntity<UpdateFileResponseDto> uploadFile(
@PostMapping("/v1/file")
public ResponseEntity<UpdateFileResponseDto> saveFile(
@AuthenticationPrincipal final User user, final MultipartFile file) {
try {
final LocalDateTime now = LocalDateTime.now();
final long userId = Long.parseLong(user.getUsername());
final FileResult result = fileService.uploadFile(file.getBytes(), userId, now);
final LocalDateTime uploadTime = LocalDateTime.now();
final long userId = parseUserId(user);
final FileResult result = fileService.uploadFile(getFileContent(file), userId, uploadTime);

return ResponseEntity.ok(mapToFileResponseDto(result));
}

@PostMapping("/v1/files")
public ResponseEntity<List<UpdateFileResponseDto>> saveFiles(
@AuthenticationPrincipal final User user, final List<MultipartFile> files) {
final LocalDateTime uploadTime = LocalDateTime.now();
final long userId = parseUserId(user);
final List<FileResult> results =
fileService.uploadFiles(userId, getFileContentAsList(files), uploadTime);

return ResponseEntity.ok(new UpdateFileResponseDto(result.id(), result.url()));
return ResponseEntity.ok(results.stream().map(this::mapToFileResponseDto).toList());
}

private long parseUserId(User user) {
return Long.parseLong(user.getUsername());
}

private byte[] getFileContent(MultipartFile file) {
try {
return file.getBytes();
} catch (IOException e) {
return ResponseEntity.internalServerError().build();
return null;
}
}

private List<byte[]> getFileContentAsList(List<MultipartFile> files) {
return files.stream().map(this::getFileContent).filter(Objects::nonNull).toList();
}

private UpdateFileResponseDto mapToFileResponseDto(FileResult result) {
return new UpdateFileResponseDto(result.id(), result.url());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.perfume.api.file.application.port.out.FileRepository;
import io.perfume.api.file.domain.File;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

Expand All @@ -21,4 +22,17 @@ public FileResult uploadFile(

return new FileResult(uploadedFile.getId(), uploadedFile.getUrl());
}

public List<FileResult> uploadFiles(
final long userId, final List<byte[]> files, final LocalDateTime now) {
return files.stream()
.map(
file -> {
final File uploadedFile =
fileRepository.save(
File.createFile("https://picsum.photos/200/300", userId, now));
return new FileResult(uploadedFile.getId(), uploadedFile.getUrl());
})
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.perfume.api.file.adapter.in.http;

import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.multipart;
Expand Down Expand Up @@ -29,7 +28,6 @@
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
@Transactional
@SpringBootTest
@SuppressWarnings("NonAsciiCharacters")
class FileControllerTest {

private MockMvc mockMvc;
Expand All @@ -49,15 +47,15 @@ void setUp(
@Test
@DisplayName("파일을 업로드한다.")
@WithMockUser(username = "1", roles = "USER")
void uploadFile() throws Exception {
void testFileUpload() throws Exception {
// given
final MockMultipartFile file =
new MockMultipartFile("file", "test.txt", "text/plain", "file".getBytes());

// when & then
mockMvc
.perform(
multipart("/v1/files")
multipart("/v1/file")
.file(file)
.characterEncoding("UTF-8")
.contentType("multipart/form-data"))
Expand All @@ -69,4 +67,31 @@ void uploadFile() throws Exception {
fieldWithPath("id").description("파일 식별자"),
fieldWithPath("url").description("파일 URL"))));
}

@Test
@DisplayName("파일을 여러 개 업로드한다.")
@WithMockUser(username = "1", roles = "USER")
void testUploadMultipleFiles() throws Exception {
// given
final MockMultipartFile file1 =
new MockMultipartFile("files", "test1.txt", "text/plain", "file1".getBytes());
final MockMultipartFile file2 =
new MockMultipartFile("files", "test2.txt", "text/plain", "file2".getBytes());

// when & then
mockMvc
.perform(
multipart("/v1/files")
.file(file1)
.file(file2)
.characterEncoding("UTF-8")
.contentType("multipart/form-data"))
.andDo(
document(
"files-upload",
requestParts(partWithName("files").description("업로드할 파일")),
responseFields(
fieldWithPath("[].id").description("파일 식별자"),
fieldWithPath("[].url").description("파일 URL"))));
}
}

0 comments on commit 755c889

Please sign in to comment.