Files
2026-05-29 17:49:25 +09:00

212 lines
7.7 KiB
Plaintext

= 파일 업/다운로드
== 개요
클라이언트의 파일 업/다운로드 API 호출을 처리한다.
=== 파일 업로드
단일 파일 업로드를 처리한다. 하나의 파일만 업로드하는 경우가 아니라면 보통은 멀티 파일 업로드를 이용한다.
=== 멀티 파일 업로드
====
컨트롤러에서 파일 업로드 서비스를 호출한다.
.FileManagerController
[source,java]
----
@PostMapping("/resource/attachments/multifile-upload")
public List<AttachFile> uploadMultiFile(
@Parameter(description = "MultipartFile[]", required = true) @RequestParam(required = true) MultipartFile[] files,
@Parameter(description = "다운로드 구분 (컴포넌트 한개 이상일 경우 구분)", required = true) @RequestParam(required = true) String downloadType) {
return fileManagerService.save(downloadType, Arrays.asList(files));
}
----
====
====
서비스에서 지정된 경로에 파일 리소스를 저장하고, 업로드된 파일 정보를 DB 에 저장한다.
.FileManagerServiceImpl
[source,java]
----
/**
* 기본 업로드 패스 설정 값
*/
@Value("${common.upload-path}")
private String fileUploadPath;
/**
* 업로드 루트 하위 폴더 자릿수 설정 값
*/
@Value("${common.upload.directory-name-len}")
private int directoryNameLen;
/**
* 사용자지정 업로드 패스 설정 여부
*/
@Value("${custom.upload-path.enabled}")
private boolean customUploadPathEnabled;
/**
* 사용자지정 업로드 패스 설정 값
*/
@Value("${custom.upload-path}")
private String customUploadPaths;
@Override
@Transactional
public List<AttachFile> save(String downloadType, List<MultipartFile> files) {
return this.save(downloadType, null, files);
}
@Override
@Transactional
public List<AttachFile> save(String downloadType, String refId, List<MultipartFile> files) {
List<AttachFile> attachFileList = new ArrayList<>();
files.stream().forEach(file -> {
AttachFile attachFile = this.save(downloadType, refId, file);
attachFileList.add(attachFile);
});
return attachFileList;
}
@Override
@Transactional
public AttachFile save(String downloadType, String refId, MultipartFile file) {
String fileId = this.store(file, downloadType); // <1>
AttachFile attachFile = new AttachFile();
attachFile.setDownloadType(downloadType);
attachFile.setFilePathName(this.rootUploadPath.toString());
attachFile.setFileExtensionName(fileId);
attachFile.setFileName(file.getOriginalFilename());
attachFile.setFileMimeTypeName(file.getContentType());
attachFile.setFileSize(file.getSize());
attachFile.setOwnerObjectPkId(refId);
return fileManagerDao.insertFileInfo(attachFile); // <2>
}
----
<1> 파일 리소스 저장
<2> 업로드된 파일 정보 DB저장
====
====
파일 리소스 저장 경로는 기본 업로드 패스 값(common.upload-path)과 하위 디렉토리 길이 설정 값(common.upload.directory-name-len)에 따라 결정된다. +
기본 업로드 패스외에 사용자정의 업로드 패스 설정도 가능하다.
.config.properties
[source,properties]
----
## File Attach Configuration
common.upload-path=/NAS/SDL/upload // <1>
common.upload.directory-name-len=2 // <2>
# custom upload path 설정
custom.upload-path.enabled=false // <3>
custom.upload-path=\ // <4>
notice=/NAS/SDL/upload/notice,\
faq=/NAS/SDL/upload/faq
----
<1> 기본 업로드 패스 설정
<2> 업로드 패스 하위 디렉토리 길이 +
파일명(UUID)에서 이 길이 만큼 잘라서 기본 업로드 패스 하위 디렉토리가 생성된다.
<3> 사용자정의 업로드 패스 사용 여부
<4> 파일 컴포넌트별 사용자정의 업로드 패스 설정 (custom.upload-path.enabled=true 일 경우 적용됨)
====
=== 파일 다운로드
컨트롤러에서 파일 다운로드 서비스를 호출한다.
.FileManagerController
[source,java]
----
@GetMapping("/resource/attachments/file-download/{fileId}")
public ResponseEntity<Resource> downloadFile(
@Parameter(description = "File ID", required = true) @PathVariable String fileId,
@Parameter(description = "다운로드 구분 (컴포넌트 한개 이상일 경우 구분)", required = true) @RequestParam(required = true) String downloadType,
HttpServletRequest request) {
long startTime = System.nanoTime();
if (StringUtils.isBlank(fileId)) {
throw new FileManagerException("No File ID");
}
AttachFile attachFile = fileManagerService.getAttachFile(fileId, downloadType); // <1>
if (attachFile == null) {
throw new FileManagerException("Cannot find file info: " + fileId);
}
Resource resource = fileManagerService.getResource(attachFile.getFileExtensionName(), attachFile.getFilePathName()); // <2>
String contentType = attachFile.getFileMimeTypeName();
if (StringUtils.isBlank(contentType)) {
contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
String fileName = attachFile.getFileName(); // <3>
String encodeFileName = null;
// 다운로드 파일명 UTF-8 인코딩
encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+", "%20");
// 다운로드 이력 로깅
fileManagerService.saveFileDownloadLog(fileName, startTime, request); // <4>
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header("Content-Transfer-Encoding", "binary")
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodeFileName + "\"")
.body(resource);
}
----
<1> 파일 정보 조회
<2> 파일 리소스 가져오기
<3> 파일 업로드 시점의 파일명
<4> 파일 다운로드 이력 로깅
=== 멀티 파일 다운로드
여러 파일을 zip 파일 형태로 다운로드할 수 있도록 제공한다.
.FileManagerController
[source,java]
----
@Value("${common.download.zipfilename}")
private String zipFileName;
@GetMapping("/resource/attachments/multifile-download")
public ResponseEntity<ByteArrayResource> downloadMultiFile(
@Parameter(description = "File IDs", required = true) @RequestParam(required = true) String[] fileIds,
@Parameter(description = "다운로드 구분 (컴포넌트 한개 이상일 경우 구분)", required = true) @RequestParam(required = true) String downloadType,
@Parameter(description = "zip 파일명", required = false) @RequestParam(required = false) String zipFileName,
HttpServletRequest request) {
long startTime = System.nanoTime();
if (fileIds == null || fileIds.length == 0) {
throw new FileManagerException("No File ID(s)");
}
if (StringUtils.isBlank(zipFileName)) {
zipFileName = this.zipFileName;
}
// 다운로드 파일명 UTF-8 인코딩
String fileName = null;
fileName = URLEncoder.encode(zipFileName, StandardCharsets.UTF_8).replace("+", "%20");
// 다운로드 이력 로깅
fileManagerService.saveFileDownloadLog(zipFileName, startTime, request);
ByteArrayResource baResource = fileManagerService.getByteArrayResource(fileIds, downloadType);
return ResponseEntity.ok()
.contentLength(baResource.contentLength())
.contentType(MediaType.parseMediaType("application/zip"))
.header("Content-Transfer-Encoding", "binary")
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + ".zip\";")
.body(baResource);
}
----