시작하기
파일 다운받기 구현 방법은 대표적으로 2가지가 있는 것 같다.
하나는 Front에서 Blob로 변환 시켜 a태그의 download속성을 사용해 구현하는 방법.
하나는 Back에서 byte로 파일을 읽어 응답하면 Front에서 바로 다운받게 하는 방법.
처음에 말한 Front에서 Blob로 변환시켜 다운받는 방법은 IE, Edge, Firefox, Chrome, Safari등 일부 버전에서 지원하지 않는다.
브라우저 지원에 영향을 받지 않는다면 download속성을 사용해 Front에서 작업을 해도 상관 없지만 브라우저 신경을 쓰고 싶지 않다면 Back에서 기능을 구현하는게 좋을 것 같다.
Front
<div>
<a href="http://localhost:8080/api/download">파일 다운로드</a>
</div>
👉 <a href="http://localhost:8080/api/download">파일 다운로드</a>
- 예제를 간단하게 하기 위해 하드코딩으로 파일 다운로드 주소를 href속성에 박았다.
- 만약, 동적으로 특정 파일을 다운받기를 원한다면 API통신에 파라미터값을 같이 보내면 된다.
- 그럼 Back에서는 해당 파라미터값으로 파일 정보를 가져와 다운받는 로직을 실행 시키면 된다.
- 현재는 고정된 단일 파일만 다운받는 예제 이다.
Back
Dependency 추가
<!-- pom.xml -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
파일을 Byte로 변환 하기 위한 dependency다. Apache Commons File Utils에서 제공한다.
code
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class Sample {
@GetMapping("/download")
public void download(HttpServletResponse response) throws IOException {
String path = "C:/Users/superpil/OneDrive/바탕 화면/file/tistory.PNG";
byte[] fileByte = FileUtils.readFileToByteArray(new File(path));
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; fileName=\"" + URLEncoder.encode("tistory.png", "UTF-8")+"\";");
response.setHeader("Content-Transfer-Encoding", "binary");
response.getOutputStream().write(fileByte);
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
👉 HttpServletResponse response
- Front에 전달할 데이터를 담고 있다.
- 아래에서 Front가 파일을 다운받을 수 있게
HttpServletResponse
객체에 Contet-Type, Content-dispositon 등 정보를 담아 전송 한다.
👉 path
- file이 실제로 위치한 경로이다.
- 경로에는 파일의 확장자까지 필요하다.
👉 byte[] fileByte = FileUtils.readFileToByteArray(new File(path))
new File()
을 이용해 path에 있는 파일 정보를 가져온다.FileUtils.readFileToByteArray()
로 파일을 Byte배열로 변환한다.
👉 response.setContentType("application/octet-stream")
👉 response.setHeader("Content-Disposition", "attachment; fileName=\"" + URLEncoder.encode("fileDownload.png", "UTF-8")+"\";")
- Front에서 파일을 다운받을 수 있게 하는 HTTP Header 설정이다.
- 만약, Header에 해당 설정이 없는경우 다운로드가 아닌 브라우저에서 파일이 열린다.Content-Type설정 없는 경우다운로드를 위해서는 반드시 Header에 위 코드를 설정해야 한다.
setContentType("application/octet-stream")
은 응답 데이터가 binary 데이터라는 의미이며, 브라우저는 ContentType을 보고 데이터를 어떻게 분석, 파싱할지 결정 한다."Content-Disposition", "attachment"
는 브라우저가 파일을 다운받게 하는 설정이다.fileName=""
은 다운로드 될 파일의 이름을 지정한다. 파일 이름은URLEncoder.encode()
를 사용해UTF-8
로 변환하여, 문자깨짐을 방지한다. 현재는fileDownload.png
이름으로 파일이 다운로드 된다.- 위 HTTP Header의 설정들은 브라우저 마다 약간씩 동작이 다를 수 있다.
Spring Boot에서 파일 다운로드를 간단하게 구현 해봤다.
기능 동작만을 위해 단순하게 로직을 짜봤는데 구현하는 중 생각 보다 신경써야 할 부분이 있었다.
대표적으로 파일 다운로드의 취약점이 있으며, 해커들의 공격을 대비하는 방어적 코드를 생각 해야 된다.
파일 다운로드의 취약점은 아래 Reference에서 확인 가능하다.
관련 참고글
- https://pygmalion0220.tistory.com/entry/HTTP-Content-Type?category=420772 - [HTTP]Content-Type
Reference
'개발노트 > Spring' 카테고리의 다른 글
[디자인 패턴] Singleton Pattern (0) | 2022.03.27 |
---|---|
[Spring Boot] Jackson 기본 개념과 LocalDateTime변환 이슈 (0) | 2022.03.21 |
[디자인 패턴] Observer Pattern (0) | 2022.03.13 |
[Spring Boot] Rest API 에 Spring Security Form 로그인 적용하기 (4) | 2021.12.27 |
[Spring Boot] @Vaild Annotation - 유효성 검사 (0) | 2020.08.23 |
주니어 개발자의 성장 기록지
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!