|
1 | 1 | package com.sprint.mission.discodeit.controller;
|
2 | 2 |
|
3 |
| -import com.sprint.mission.discodeit.entity.BinaryContent; |
| 3 | +import com.sprint.mission.discodeit.dto.BinaryContentDownload; |
| 4 | +import com.sprint.mission.discodeit.dto.data.BinaryContentDto; |
| 5 | +import com.sprint.mission.discodeit.dto.request.BinaryContentCreateRequest; |
4 | 6 | import com.sprint.mission.discodeit.service.BinaryContentService;
|
| 7 | +import com.sprint.mission.discodeit.storage.LocalBinaryContentStorage; |
5 | 8 | import com.sprint.mission.discodeit.util.LogMapUtil;
|
6 | 9 | import io.swagger.v3.oas.annotations.tags.Tag;
|
| 10 | +import java.io.IOException; |
7 | 11 | import lombok.RequiredArgsConstructor;
|
8 | 12 | import org.slf4j.Logger;
|
9 | 13 | import org.slf4j.LoggerFactory;
|
| 14 | +import org.springframework.core.io.Resource; |
| 15 | +import org.springframework.http.HttpHeaders; |
| 16 | +import org.springframework.http.HttpStatus; |
| 17 | +import org.springframework.http.MediaType; |
10 | 18 | import org.springframework.http.ResponseEntity;
|
11 | 19 | import org.springframework.web.bind.annotation.*;
|
12 | 20 |
|
13 | 21 | import java.util.List;
|
14 | 22 | import java.util.UUID;
|
| 23 | +import org.springframework.web.multipart.MultipartFile; |
15 | 24 |
|
16 | 25 | @RestController
|
17 | 26 | @RequiredArgsConstructor
|
|
20 | 29 | public class BinaryContentController {
|
21 | 30 |
|
22 | 31 | private final BinaryContentService binaryContentService;
|
| 32 | + private final LocalBinaryContentStorage localBinaryContentStorage; |
23 | 33 | private static final Logger log = LoggerFactory.getLogger(BinaryContentController.class);
|
24 | 34 |
|
| 35 | + @PostMapping |
| 36 | + public ResponseEntity<BinaryContentDto> create(@RequestBody BinaryContentCreateRequest request) { |
| 37 | + BinaryContentDto createdContent = binaryContentService.create(request); |
| 38 | + log.info("{}", LogMapUtil.of("action", "create") |
| 39 | + .add("request", request) |
| 40 | + .add("contentId", createdContent.id())); |
| 41 | + return new ResponseEntity<>(createdContent, HttpStatus.CREATED); |
| 42 | + } |
| 43 | + |
25 | 44 | @GetMapping("/{binaryContentId}")
|
26 |
| - public ResponseEntity<BinaryContent> read(@PathVariable UUID binaryContentId) { |
27 |
| - BinaryContent content = binaryContentService.find(binaryContentId); |
| 45 | + public ResponseEntity<BinaryContentDto> read(@PathVariable UUID binaryContentId) { |
| 46 | + BinaryContentDto content = binaryContentService.findById(binaryContentId); |
28 | 47 | log.info("{}", LogMapUtil.of("action", "read")
|
29 | 48 | .add("content", content));
|
30 | 49 | return ResponseEntity.ok(content);
|
31 | 50 | }
|
32 | 51 |
|
33 | 52 | @GetMapping
|
34 |
| - public ResponseEntity<List<BinaryContent>> readAll(@RequestParam List<UUID> binaryContentIds) { |
35 |
| - List<BinaryContent> contents = binaryContentService.findAllByKeys(binaryContentIds); |
| 53 | + public ResponseEntity<List<BinaryContentDto>> readAll(@RequestParam List<UUID> binaryContentIds) { |
| 54 | + List<BinaryContentDto> contents = binaryContentService.findAllByIds(binaryContentIds); |
36 | 55 | log.info("{}", LogMapUtil.of("action", "readAll")
|
37 | 56 | .add("contents", contents));
|
38 | 57 | return ResponseEntity.ok(contents);
|
39 | 58 | }
|
| 59 | + |
| 60 | + @GetMapping("/{binaryContentId}/download") // {binaryContentId}는 경로 변수로 사용됩니다. |
| 61 | + public ResponseEntity<Resource> downloadFile(@PathVariable UUID binaryContentId) { |
| 62 | + try { |
| 63 | + // 1. 서비스 레이어에게 다운로드에 필요한 정보 (데이터 스트림, 파일명, 타입)를 요청합니다. |
| 64 | + // 서비스는 Repository에서 메타데이터를, Storage에서 실제 데이터를 가져와 BinaryContentDownload DTO로 반환합니다. |
| 65 | + BinaryContentDownload downloadData = binaryContentService.download(binaryContentId); |
| 66 | + |
| 67 | + // 2. 서비스로부터 받은 정보를 바탕으로 HTTP 응답 헤더를 설정합니다. |
| 68 | + HttpHeaders headers = new HttpHeaders(); |
| 69 | + // Content-Disposition 헤더 설정: 파일을 다운로드하도록 지정하고 파일명을 명시합니다. |
| 70 | + // 파일명에 특수 문자가 포함될 경우 인코딩 처리가 필요할 수 있습니다 (여기서는 단순화). |
| 71 | + headers.add(HttpHeaders.CONTENT_DISPOSITION, |
| 72 | + "attachment; filename=\"" + downloadData.filename() + "\""); |
| 73 | + // Content-Type 헤더 설정: 파일의 MIME 타입을 명시합니다. |
| 74 | + // headers.add(HttpHeaders.CONTENT_TYPE, downloadData.getContentType()); // 아래 contentType()으로 설정 가능 |
| 75 | + |
| 76 | + // 3. ResponseEntity 객체를 구성하여 반환합니다. |
| 77 | + return ResponseEntity.ok() // HTTP 상태 코드 200 OK |
| 78 | + .headers(headers) // 설정한 헤더 추가 |
| 79 | + // 서비스에서 제공된 컨텐츠 타입을 사용하여 응답의 Content-Type 헤더를 설정합니다. |
| 80 | + .contentType(MediaType.parseMediaType(downloadData.contentType())) |
| 81 | + // 서비스에서 제공된 InputStreamResource (파일 데이터)를 응답 본문에 설정합니다. |
| 82 | + .body(downloadData.getResource()); // BinaryContentDownload에서 제공하는 Resource 객체 사용 |
| 83 | + |
| 84 | + } catch (IllegalArgumentException e) { |
| 85 | + // BinaryContent 메타데이터를 서비스에서 찾을 수 없는 경우 (서비스에서 throw) |
| 86 | + // logger.warn("Download failed: Metadata not found for id: " + binaryContentId, e); // 로깅 고려 |
| 87 | + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // HTTP 상태 코드 404 Not Found 응답 |
| 88 | + } catch (IOException e) { |
| 89 | + // 스토리지에서 데이터 로딩 중 I/O 오류 발생 또는 데이터가 없는 경우 (서비스에서 throw) |
| 90 | + // 서비스에서 데이터 부재와 다른 I/O 오류를 구분하여 던진다면 여기서 더 세분화된 응답 가능 (예: 데이터 부재 시 404) |
| 91 | + // 현재는 모든 I/O 오류를 500으로 처리하거나, 데이터 부재도 포함하여 404로 처리할 수 있습니다. |
| 92 | + // logger.error("Download failed: Storage error for id: " + binaryContentId, e); // 로깅 고려 |
| 93 | + // 여기서는 I/O 오류 전반을 Internal Server Error로 처리합니다. |
| 94 | + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) |
| 95 | + .body(null); // HTTP 상태 코드 500 Internal Server Error 응답 |
| 96 | + } catch (Exception e) { |
| 97 | + // 그 외 예상치 못한 오류 발생 시 |
| 98 | + // logger.error("Unexpected error during download for id: " + binaryContentId, e); // 로깅 고려 |
| 99 | + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) |
| 100 | + .body(null); // HTTP 상태 코드 500 Internal Server Error 응답 |
| 101 | + } |
| 102 | + } |
40 | 103 | }
|
0 commit comments