[api] FileController.java 컨벤션 맞춰 변경

This commit is contained in:
2026-05-22 16:40:22 +09:00
parent 24783da7a0
commit 059d690c93
5 changed files with 41 additions and 33 deletions
@@ -44,7 +44,7 @@ public class OpenApiConfig {
.pathsToExclude( .pathsToExclude(
"/admin/**" "/admin/**"
, "/cors/**" , "/cors/**"
, "/files/**" , "/file/**"
, "/tusFiles/**" , "/tusFiles/**"
, "/error" , "/error"
, "/actuator/**" , "/actuator/**"
@@ -66,7 +66,7 @@ public class OpenApiConfig {
.group("900. common") .group("900. common")
.pathsToMatch( .pathsToMatch(
"/cors/**" "/cors/**"
, "/files/**" , "/file/**"
, "/tusFiles/**" , "/tusFiles/**"
) )
.build(); .build();
@@ -90,7 +90,7 @@ public class SecurityConfig {
, "/user/migrationUserList" , "/user/migrationUserList"
, "/tusFiles/tusHook" , "/tusFiles/tusHook"
, "/tusFiles/uploadAuth" , "/tusFiles/uploadAuth"
, "/files/**" , "/file/**"
).permitAll() ).permitAll()
.anyRequest().authenticated() .anyRequest().authenticated()
) )
@@ -28,7 +28,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
try { try {
String requestUri = request.getRequestURI(); String requestUri = request.getRequestURI();
boolean adminRequest = requestUri.startsWith("/admin/"); boolean adminRequest = requestUri.startsWith("/admin/");
boolean sharedRequest = requestUri.startsWith("/tusFiles/") || requestUri.startsWith("/files/") || requestUri.startsWith("/cors/"); boolean sharedRequest = requestUri.startsWith("/tusFiles/") || requestUri.startsWith("/file/") || requestUri.startsWith("/cors/");
String token = resolveToken(request, adminRequest, sharedRequest); String token = resolveToken(request, adminRequest, sharedRequest);
if (token != null && jwtTokenProvider.validateToken(token)) { if (token != null && jwtTokenProvider.validateToken(token)) {
@@ -17,11 +17,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
@Tag( @Tag(
name = "902. 파일 업로드", name = "902. 파일 업로드"
description = "DB 기록 없이 파일을 저장하고 uploadPath를 반환하는 단순 업로드 API입니다. SunEditor 이미지는 file-domain URL을 반환합니다." , description = "DB 기록 없이 파일을 저장하고 uploadPath를 반환하는 단순 업로드 API입니다. SunEditor 이미지는 file-domain URL을 반환합니다."
) )
@RestController @RestController
@RequestMapping("/files") @RequestMapping("/file")
public class FileController { public class FileController {
private final FileService fileService; private final FileService fileService;
@@ -30,75 +30,79 @@ public class FileController {
} }
@Operation( @Operation(
summary = "단순 파일 업로드", summary = "단순 파일 업로드"
description = """ , description = """
DB에 기록하지 않고 파일만 저장합니다. DB에 기록하지 않고 파일만 저장합니다.
반환된 uploadPath는 이후 각 업무 테이블에 저장해서 view/download 경로로 사용할 수 있습니다. 반환된 uploadPath는 이후 각 업무 테이블에 저장해서 view/download 경로로 사용할 수 있습니다.
folder 값은 저장 폴더 키이며, 설정된 타입이면 해당 정책을 사용하고 설정되지 않은 값이면 전역 정책만 적용합니다. folder 값은 저장 폴더 키이며, 설정된 타입이면 해당 정책을 사용하고 설정되지 않은 값이면 전역 정책만 적용합니다.
""" """
) )
@PostMapping("/upload") @PostMapping("/upload")
public ResponseEntity<ApiResponse<FileUploadVo>> upload( public ResponseEntity<ApiResponse<FileUploadVo>> fileUpload(
@RequestParam String folder, @RequestParam String folder
@RequestPart MultipartFile file , @RequestPart MultipartFile file
) { ) {
FileUploadVo result = fileService.upload(folder, file); FileUploadVo fileUploadVo = fileService.saveFileUpload(folder, file);
return ApiResponse.entity(result, ApiResponseCode.CODE_200); return ApiResponse.entity(fileUploadVo, ApiResponseCode.CODE_200);
} }
@Operation( @Operation(
summary = "SunEditor 이미지 업로드", summary = "SunEditor 이미지 업로드"
description = """ , description = """
SunEditor 이미지 업로드 전용 API입니다. SunEditor 이미지 업로드 전용 API입니다.
업로드 파일을 저장한 뒤 SunEditor가 요구하는 result 배열 형태로 응답합니다. 업로드 파일을 저장한 뒤 SunEditor가 요구하는 result 배열 형태로 응답합니다.
응답 URL은 file.upload.view.file-domain 값과 uploadPath를 조합한 공개 file-domain URL입니다. 응답 URL은 file.upload.view.file-domain 값과 uploadPath를 조합한 공개 file-domain URL입니다.
""" """
) )
@PostMapping("/suneditor/upload") @PostMapping("/suneditor/upload")
public ResponseEntity<SunEditorUploadVo> sunEditorUpload( public ResponseEntity<SunEditorUploadVo> fileSunEditorUpload(
@RequestParam(value = "folder", defaultValue = "editor") String folder, @RequestParam(value = "folder", defaultValue = "editor") String folder
MultipartHttpServletRequest request , MultipartHttpServletRequest request
) { ) {
List<SunEditorUploadVo.Item> result = new ArrayList<>(); List<SunEditorUploadVo.Item> sunEditorUploadItemList = new ArrayList<>();
for (MultipartFile file : request.getFileMap().values()) { for (MultipartFile file : request.getFileMap().values()) {
FileUploadVo uploaded = fileService.upload(folder, file); FileUploadVo uploaded = fileService.saveFileUpload(folder, file);
String imageUrl = fileService.buildImageUrl(uploaded.getUploadPath()); String imageUrl = fileService.buildImageUrl(uploaded.getUploadPath());
result.add(new SunEditorUploadVo.Item( sunEditorUploadItemList.add(new SunEditorUploadVo.Item(
imageUrl, imageUrl,
uploaded.getOriginalFileName(), uploaded.getOriginalFileName(),
uploaded.getFileSize() uploaded.getFileSize()
)); ));
} }
return ResponseEntity.ok(new SunEditorUploadVo(result)); return ResponseEntity.ok(new SunEditorUploadVo(sunEditorUploadItemList));
} }
@Operation( @Operation(
summary = "파일 보기", summary = "파일 보기"
description = """ , description = """
uploadPath 기준으로 파일을 inline 응답합니다. uploadPath 기준으로 파일을 inline 응답합니다.
예: /uploads/notice/2026/05/08/sample.png 예: /uploads/notice/2026/05/08/sample.png
단순 업로드 결과의 uploadPath를 그대로 path 파라미터에 전달합니다. 단순 업로드 결과의 uploadPath를 그대로 path 파라미터에 전달합니다.
""" """
) )
@GetMapping("/view") @GetMapping("/view")
public ResponseEntity<Resource> view(@RequestParam String path) { public ResponseEntity<Resource> fileView(
@RequestParam String path
) {
return fileService.resource(path, true); return fileService.resource(path, true);
} }
@Operation( @Operation(
summary = "파일 다운로드", summary = "파일 다운로드"
description = """ , description = """
uploadPath 기준으로 파일을 attachment 응답합니다. uploadPath 기준으로 파일을 attachment 응답합니다.
예: /uploads/notice/2026/05/08/sample.pdf 예: /uploads/notice/2026/05/08/sample.pdf
단순 업로드 결과의 uploadPath를 그대로 path 파라미터에 전달합니다. 단순 업로드 결과의 uploadPath를 그대로 path 파라미터에 전달합니다.
""" """
) )
@GetMapping("/download") @GetMapping("/download")
public ResponseEntity<Resource> download(@RequestParam String path) { public ResponseEntity<Resource> fileDownload(
@RequestParam String path
) {
return fileService.resource(path, false); return fileService.resource(path, false);
} }
} }
@@ -4,14 +4,18 @@ import com.alist.api.modules.file.properties.FileUploadProperties;
import com.alist.api.modules.file.vo.FileUploadVo; import com.alist.api.modules.file.vo.FileUploadVo;
import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.*; import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.*; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
@@ -28,7 +32,7 @@ public class FileService {
this.fileUploadImageService = fileUploadImageService; this.fileUploadImageService = fileUploadImageService;
} }
public FileUploadVo upload(String folder, MultipartFile file) { public FileUploadVo saveFileUpload(String folder, MultipartFile file) {
if (folder == null || folder.isBlank()) throw new IllegalArgumentException("invalid folder"); if (folder == null || folder.isBlank()) throw new IllegalArgumentException("invalid folder");
if (file == null || file.isEmpty()) throw new IllegalArgumentException("empty file"); if (file == null || file.isEmpty()) throw new IllegalArgumentException("empty file");