Skip to content

Commit f61ebd1

Browse files
committed
fix: 파일저장 인코딩 수정
1 parent f1707fb commit f61ebd1

File tree

8 files changed

+142
-124
lines changed

8 files changed

+142
-124
lines changed

src/main/java/dmu/dasom/api/domain/news/dto/NewsRequestDto.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ public class NewsRequestDto {
2727

2828
@Schema(description = "이미지 파일 ID 목록", example = "[1, 2, 3]", nullable = true)
2929
private List<Long> fileIds;
30+
3031
}

src/main/java/dmu/dasom/api/domain/news/dto/NewsResponseDto.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package dmu.dasom.api.domain.news.dto;
22

33
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Builder;
45
import lombok.Getter;
56

67
import java.time.LocalDateTime;
78
import java.util.List;
89

910
@Getter
11+
@Builder
1012
@Schema(name = "NewsResponseDto", description = "뉴스 응답 DTO")
1113
public class NewsResponseDto {
1214

@@ -22,7 +24,7 @@ public class NewsResponseDto {
2224
@Schema(description = "작성일", example = "2025-02-14T12:00:00")
2325
private LocalDateTime createdAt;
2426

25-
@Schema(description = "뉴스 이미지 URL", example = "['https://example.com/image.jpg', 'https://example.com/image2.jpg']", nullable = true)
27+
@Schema(description = "Base64 인코딩된 이미지", example = "[data:image/jpeg;base64,xxxxxx]", nullable = true)
2628
private List<String> imageUrls;
2729

2830
public NewsResponseDto(Long id, String title, String content, LocalDateTime createdAt, List<String> imageUrls) {

src/main/java/dmu/dasom/api/domain/news/entity/NewsEntity.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class NewsEntity extends BaseEntity {
3030

3131
@ElementCollection
3232
@CollectionTable(name = "news_images", joinColumns = @JoinColumn(name = "news_id"))
33-
@Column(name = "image_url", length = 255)
33+
@Column(name = "image_data", columnDefinition = "TEXT")
3434
private List<String> imageUrls;
3535

3636
public void update(String title, String content, List<String> imageUrls) {
@@ -42,4 +42,5 @@ public void update(String title, String content, List<String> imageUrls) {
4242
public NewsResponseDto toResponseDto() {
4343
return new NewsResponseDto(id, title, content, getCreatedAt(), imageUrls);
4444
}
45+
4546
}

src/main/java/dmu/dasom/api/domain/news/service/NewsService.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,43 @@ public class NewsService {
2525
// 전체 뉴스 조회
2626
public List<NewsResponseDto> getAllNews() {
2727
return newsRepository.findAll().stream()
28-
.map(NewsEntity::toResponseDto)
28+
.map(news -> NewsResponseDto.builder()
29+
.id(news.getId())
30+
.title(news.getTitle())
31+
.content(news.getContent())
32+
.createdAt(news.getCreatedAt())
33+
.imageUrls(news.getImageUrls())
34+
.build())
2935
.collect(Collectors.toList());
3036
}
3137

3238
// 개별 뉴스 조회
3339
public NewsResponseDto getNewsById(Long id) {
34-
return newsRepository.findById(id)
35-
.map(NewsEntity::toResponseDto)
40+
NewsEntity news = newsRepository.findById(id)
3641
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
42+
43+
return NewsResponseDto.builder()
44+
.id(news.getId())
45+
.title(news.getTitle())
46+
.content(news.getContent())
47+
.createdAt(news.getCreatedAt())
48+
.imageUrls(news.getImageUrls())
49+
.build();
3750
}
3851

3952
// 뉴스 생성
4053
@Transactional
4154
public NewsResponseDto createNews(NewsRequestDto requestDto) {
4255
List<FileEntity> uploadedFiles = fileService.getFilesByIds(requestDto.getFileIds());
43-
List<String> imageUrls = uploadedFiles.stream().map(FileEntity::getFilePath).collect(Collectors.toList());
56+
57+
List<String> base64Images = uploadedFiles.stream()
58+
.map(file -> "data:" + file.getFileType() + ";base64," + file.getBase64Data())
59+
.collect(Collectors.toList());
4460

4561
NewsEntity news = NewsEntity.builder()
4662
.title(requestDto.getTitle())
4763
.content(requestDto.getContent())
48-
.imageUrls(imageUrls)
64+
.imageUrls(base64Images)
4965
.build();
5066

5167
return newsRepository.save(news).toResponseDto();
@@ -58,9 +74,12 @@ public NewsResponseDto updateNews(Long id, NewsRequestDto requestDto) {
5874
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND));
5975

6076
List<FileEntity> uploadedFiles = fileService.getFilesByIds(requestDto.getFileIds());
61-
List<String> imageUrls = uploadedFiles.stream().map(FileEntity::getFilePath).collect(Collectors.toList());
6277

63-
news.update(requestDto.getTitle(), requestDto.getContent(), imageUrls);
78+
List<String> base64Images = uploadedFiles.stream()
79+
.map(file -> "data:" + file.getFileType() + ";base64," + file.getBase64Data())
80+
.collect(Collectors.toList());
81+
82+
news.update(requestDto.getTitle(), requestDto.getContent(), base64Images);
6483

6584
return news.toResponseDto();
6685
}
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
package dmu.dasom.api.global.file.controller;
22

33
import dmu.dasom.api.global.file.service.FileService;
4-
import io.swagger.v3.oas.annotations.Operation;
5-
import io.swagger.v3.oas.annotations.tags.Tag;
64
import lombok.RequiredArgsConstructor;
75
import org.springframework.http.ResponseEntity;
86
import org.springframework.web.bind.annotation.*;
97
import org.springframework.web.multipart.MultipartFile;
108

119
import java.util.List;
1210

13-
@Tag(name = "File API", description = "파일 업로드 API")
1411
@RestController
1512
@RequestMapping("/api/global/file")
1613
@RequiredArgsConstructor
1714
public class FileController {
1815

1916
private final FileService fileService;
2017

21-
@Operation(summary = "파일 업로드", description = "여러 개의 파일 업로드 + 파일 ID 리스트를 반환")
18+
// 파일 업로드
2219
@PostMapping("/upload")
2320
public ResponseEntity<List<Long>> uploadFiles(@RequestParam("files") List<MultipartFile> files) {
2421
List<Long> fileIds = fileService.uploadFiles(files);
2522
return ResponseEntity.ok(fileIds);
2623
}
2724

25+
// 파일 조회
26+
@GetMapping("/{fileId}")
27+
public ResponseEntity<String> getFile(@PathVariable Long fileId) {
28+
return ResponseEntity.ok(fileService.getFileBase64(fileId));
29+
}
30+
2831
}

src/main/java/dmu/dasom/api/global/file/entity/FileEntity.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import jakarta.persistence.*;
44
import lombok.*;
55

6+
@Getter
67
@Entity
78
@Table(name = "files")
8-
@Getter
99
@NoArgsConstructor
1010
@AllArgsConstructor
1111
@Builder
@@ -14,15 +14,18 @@ public class FileEntity {
1414
@Id
1515
@GeneratedValue(strategy = GenerationType.IDENTITY)
1616
private Long id;
17+
1718
@Column(nullable = false)
1819
private String originalName;
19-
@Column(nullable = false)
20-
private String storedName;
21-
@Column(nullable = false)
22-
private String filePath;
20+
21+
@Lob
22+
@Column(name = "base64data", nullable = false, columnDefinition = "CLOB")
23+
private String base64Data;
24+
2325
@Column(nullable = false)
2426
private String fileType;
27+
2528
@Column(nullable = false)
2629
private Long fileSize;
2730

28-
}
31+
}

src/main/java/dmu/dasom/api/global/file/service/FileService.java

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@
66
import org.springframework.stereotype.Service;
77
import org.springframework.web.multipart.MultipartFile;
88

9-
import java.io.File;
109
import java.io.IOException;
11-
import java.nio.file.Files;
12-
import java.nio.file.Path;
13-
import java.nio.file.StandardCopyOption;
10+
import java.util.Base64;
1411
import java.util.List;
15-
import java.util.UUID;
1612
import java.util.stream.Collectors;
1713

1814
@Service
@@ -21,44 +17,36 @@ public class FileService {
2117

2218
private final FileRepository fileRepository;
2319

24-
//파일은 db로 저장하고 일단 물리적으로 uploads/ 에 저장되게
25-
private static final String FILE_DIR = "uploads/";
26-
2720
public List<Long> uploadFiles(List<MultipartFile> files) {
28-
return files.stream()
29-
.map(this::storeFile)
30-
.map(fileRepository::save)
31-
.map(FileEntity::getId)
32-
.collect(Collectors.toList());
33-
}
34-
35-
private FileEntity storeFile(MultipartFile file) {
36-
try {
37-
File uploadDir = new File(FILE_DIR);
38-
if (!uploadDir.exists()) {
39-
uploadDir.mkdirs();
21+
List<FileEntity> savedFiles = files.stream().map(file -> {
22+
try {
23+
byte[] bytes = file.getBytes();
24+
String base64Encoded = Base64.getEncoder().encodeToString(bytes);
25+
return FileEntity.builder()
26+
.originalName(file.getOriginalFilename())
27+
.base64Data(base64Encoded)
28+
.fileType(file.getContentType())
29+
.fileSize(file.getSize())
30+
.build();
31+
} catch (IOException e) {
32+
throw new RuntimeException("파일 인코딩 실패", e);
4033
}
34+
}).collect(Collectors.toList());
4135

42-
String originalFilename = file.getOriginalFilename();
43-
String storedFilename = UUID.randomUUID().toString() + "_" + originalFilename;
44-
Path filePath = Path.of(FILE_DIR + storedFilename);
36+
return fileRepository.saveAll(savedFiles).stream().map(FileEntity::getId).collect(Collectors.toList());
37+
}
4538

46-
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
39+
// 파일 하나 조회
40+
public String getFileBase64(Long fileId) {
41+
FileEntity file = fileRepository.findById(fileId)
42+
.orElseThrow(() -> new RuntimeException("파일을 찾을 수 없음"));
4743

48-
return FileEntity.builder()
49-
.originalName(originalFilename)
50-
.storedName(storedFilename)
51-
.filePath(filePath.toString())
52-
.fileType(file.getContentType())
53-
.fileSize(file.getSize())
54-
.build();
55-
} catch (IOException e) {
56-
throw new RuntimeException("파일 저장 중 오류 발생: " + e.getMessage(), e);
57-
}
44+
return "data:" + file.getFileType() + ";base64," + file.getBase64Data();
5845
}
5946

47+
// 파일 여러개 조회
6048
public List<FileEntity> getFilesByIds(List<Long> fileIds) {
6149
return fileRepository.findAllById(fileIds);
6250
}
63-
51+
6452
}

0 commit comments

Comments
 (0)