반응형
11-08 05:45
- Today
- Total
Link
개발하는 고라니
[Spring] 서버 -> 클라이언트 파일 다운로드 본문
반응형
위와 같은 웹 페이지가 있을 때, 첨부 파일의 이름을 클릭하면 파일이 다운로드 되도록 하는 법을 알아본다.
우선 첨부파일의 정보를 출력하는 웹 페이지의 HTML일부를 보면 다음과 같다.
<div class="uploadResult">
<ul>
<li th:each="uploadDTO : ${dto.uploadList}">
<div class="uploadDiv" th:data-url="${uploadDTO.getImageURL()}">
<a th:href="@{/download(fileName=${uploadDTO.getImageURL()})}">[[${uploadDTO.fileName}]]</a>
</div>
<img th:if="${uploadDTO.image}" th:src="|/display?fileName=${uploadDTO.getThumbnailURL()}">
<img th:if="${!uploadDTO.image}" th:src="|/display?fileName=${uploadDTO.getAttachURL()}">
</li>
</ul>
</div>
"th:"와 같은 문자열이 있는데, 이는 thymeleaf라는 Template Engine을 사용하여 그렇다. 어쨋든
<a th:href="@{/download(fileName=${uploadDTO.getImageURL()})}">[[${uploadDTO.fileName}]]</a>
//-------------------------------------------------------
<a href="/download?fileName=파일이름">파일이름</a>
'파일이름'을 클릭하면 '/download?fileName=파일이름'이라는 URL에 'GET'요청을 하게된다. 이 때 파일이 다운로드 되도록 하는 것이다.
View 단은 이정도면 되었고, Back단을 살펴보자.
Back-End
- 서버에서 MIME 타입을 다운로드 타입 "application/octet-stream"으로 지정한다.
- ResponseEntity<T>의 'T'는 byte[], Resource등 사용할 수 있다.
- 다운로드시 저장되는 이름은 Response Header의 'Content-Disposition'에 명시를 해야한다.
- 각 브라우저마다 '한글' 파일명에 대해 인코딩 처리를 한다.
//produces, MediaType.APPLICATION_OCTET_STREAM_VALUE : Header의 Content-Type 지정
//header.add("Content-Disposition", "attachment; filename=" + onlyFileName)
//: Header의 "Content-Disposition"에 저장될 파일명을 명시한다.
//result = new ResponseEntity<>(file, header, HttpStatus.OK)
//: 클라이언트에게 파일 데이터와 헤더 정보를 함께 보낸다.
@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> download(String fileName){
ResponseEntity<Resource> result = null;
try {
String originFileName = URLDecoder.decode(fileName, "UTF-8");
Resource file = new FileSystemResource("C:\\upload" + File.separator + originFileName);
if(!file.exists()) return new ResponseEntity<>(HttpStatus.NOT_FOUND);
String onlyFileName = originFileName.substring(originFileName.lastIndexOf("_") + 1);
HttpHeaders header = new HttpHeaders();
header.add("Content-Disposition", "attachment; filename=" + onlyFileName);
result = new ResponseEntity<>(file, header, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
위의 코드는 (4)에 해당하는 한글 파일명 인코딩 처리를 하지 않은 것이다. 이 때 파일명이 한글인 것을 다운받으면 어떻게 되는지 알아보자.
* Internet Explore
* Chrome
* Edge
* firefox
총 4개의 브라우저에서 시도했을 때 모두 한글명으로 나오지 않았다. 즉 각 브라우저에서 자신들만의 방법으로 처리하여 저장되는 것을 확인할 수 있었다.
이제 (4) 브라우저별 한글이름을 인코딩해보자.
@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> download(String fileName, @RequestHeader("User-Agent") String agent){
ResponseEntity<Resource> result = null;
try {
String originFileName = URLDecoder.decode(fileName, "UTF-8");
Resource file = new FileSystemResource("C:\\upload" + File.separator + originFileName);
if(!file.exists()) return new ResponseEntity<>(HttpStatus.NOT_FOUND);
//브라우저별 한글파일 명 처리
String onlyFileName = originFileName.substring(originFileName.lastIndexOf("_") + 1);
if(agent.contains("Trident"))//Internet Explore
onlyFileName = URLEncoder.encode(onlyFileName, "UTF-8").replaceAll("\\+", " ");
else if(agent.contains("Edge")) //Micro Edge
onlyFileName = URLEncoder.encode(onlyFileName, "UTF-8");
else //Chrome
onlyFileName = new String(onlyFileName.getBytes("UTF-8"), "ISO-8859-1");
//브라우저별 한글파일 명 처리
HttpHeaders header = new HttpHeaders();
header.add("Content-Disposition", "attachment; filename=" + onlyFileName);
result = new ResponseEntity<>(file, header, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
download 메소드에서 인자로 클라이언트의 브라우저가 무엇인지 정보를 받는 @RequestHeader("User-Agent") String agent를 받았다. agent의 값에 따라 인코딩 방법을 달리하면 된다.
InputStream & OutputStream 이용하여 파일 다운로드
@GetMapping(value = "/download")
public void download(String fileName, HttpServletResponse response, HttpServletRequest request){
try {
String originFileName = URLDecoder.decode(fileName, "UTF-8");
String onlyFileName = originFileName.substring(originFileName.lastIndexOf("_") + 1);
File file = new File("C:\\upload", originFileName);
if(file.exists()) {
String agent = request.getHeader("User-Agent");
//브라우저별 한글파일 명 처리
if(agent.contains("Trident"))//Internet Explore
onlyFileName = URLEncoder.encode(onlyFileName, "UTF-8").replaceAll("\\+", " ");
else if(agent.contains("Edge")) //Micro Edge
onlyFileName = URLEncoder.encode(onlyFileName, "UTF-8");
else //Chrome
onlyFileName = new String(onlyFileName.getBytes("UTF-8"), "ISO-8859-1");
//브라우저별 한글파일 명 처리
response.setHeader("Content-Type", "application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + onlyFileName);
InputStream is = new FileInputStream(file);
OutputStream os = response.getOutputStream();
int length;
byte[] buffer = new byte[1024];
while( (length = is.read(buffer)) != -1){
os.write(buffer, 0, length);
}
os.flush();
os.close();
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
이 때, Chrome에서 실행 시 agent는 다음과 같은 값을 가진다.
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome.....
반응형
'Framework > Spring' 카테고리의 다른 글
[Spring] Transactional (0) | 2021.06.11 |
---|---|
[Spring] DispatcherServlet (0) | 2021.06.04 |
[Spring] 업로드한 파일의 Content-Type (0) | 2021.02.25 |
[Spring] 파일 업로드 (0) | 2021.02.25 |
[Spring] URL Encode : 공백을 '+'이 아닌 '%20' (0) | 2020.12.17 |
Comments