Initial commit
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
= 파일 업/다운로드
|
||||
|
||||
== 개요
|
||||
클라이언트의 파일 업/다운로드 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);
|
||||
}
|
||||
----
|
||||
Reference in New Issue
Block a user