Loading...
Loading...
02-reusable-code-java/06-service/FileUploadService.java
/**
* 파일 업로드 서비스 (단일/다중 업로드, 삭제, JSON 변환)
* - FileUtil을 래핑하여 비즈니스 레벨 파일 관리 제공
* - 업로드 결과를 Map 형태로 반환 (name, url, type, size)
* - 첨부파일 목록 JSON 직렬화/역직렬화
*
* @source kcsi-smpa-internal
* @extracted 2026-03-08
* @version 1.0.0
*
* @dependencies spring-boot-starter-web, com.fasterxml.jackson.core:jackson-databind, lombok
* @see com.example.app.util.FileUtil
*/
package com.example.app.service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.example.app.util.FileUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 파일 업로드 서비스
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FileUploadService {
private final FileUtil fileUtil;
private final ObjectMapper objectMapper;
/**
* 파일 업로드 (단일)
* @param file 업로드할 파일
* @return 파일 메타데이터 (name, url, type, size)
*/
public Map<String, String> uploadFile(MultipartFile file) throws IOException {
String relativePath = fileUtil.uploadFile(file);
Map<String, String> result = new HashMap<>();
result.put("name", file.getOriginalFilename());
result.put("url", "/uploads/" + relativePath);
result.put("type", file.getContentType());
result.put("size", String.valueOf(file.getSize()));
log.info("파일 업로드: name={}, path={}", file.getOriginalFilename(), relativePath);
return result;
}
/**
* 파일 업로드 (다중)
* @param files 업로드할 파일 목록
* @return 파일 메타데이터 목록
*/
public List<Map<String, String>> uploadFiles(List<MultipartFile> files) throws IOException {
if (files.size() > fileUtil.getMaxFiles()) {
throw new IllegalArgumentException("최대 " + fileUtil.getMaxFiles() + "개까지 업로드 가능합니다.");
}
List<Map<String, String>> results = new ArrayList<>();
for (MultipartFile file : files) {
if (!file.isEmpty()) {
results.add(uploadFile(file));
}
}
return results;
}
/**
* 파일 삭제
* @param url 파일 URL (예: "/uploads/2026/03/uuid.png")
* @return 삭제 성공 여부
*/
public boolean deleteFile(String url) {
// URL에서 상대 경로 추출
String relativePath = url.replace("/uploads/", "");
boolean deleted = fileUtil.deleteFile(relativePath);
if (deleted) {
log.info("파일 삭제: path={}", relativePath);
}
return deleted;
}
/**
* 첨부파일 리스트를 JSON 문자열로 변환
*/
public String toJson(List<Map<String, String>> attachments) {
try {
return objectMapper.writeValueAsString(attachments);
} catch (Exception e) {
return "[]";
}
}
/**
* JSON 문자열을 첨부파일 리스트로 변환
*/
public List<Map<String, Object>> fromJson(String json) {
if (json == null || json.isEmpty()) {
return new ArrayList<>();
}
try {
return objectMapper.readValue(json, new TypeReference<List<Map<String, Object>>>() {});
} catch (Exception e) {
return new ArrayList<>();
}
}
}