pdf 파일 다운로드 및 현재 탭에서 열기
스프링에서 파일 다운로드 구현은 어렵지 않다.
다운로드 파일 유형이 pdf인 경우, 다운로드하지 않고 현재 탭에 미리보기로 띄워야 하는 경우도 있는데, 이도 어렵지 않다.
파일 다운로드
간단하게 ResourceLoader를 사용하여 파일 다운로드하는 방법을 알아본다.
html 부분이다.
<a href="/download/myFile.pdf">파일 다운로드</a>
<a> 태그를 작성하고, 해당 태그 클릭 시 파일 다운로드를 진행하게 한다.
myFile.pdf 부분은 파일명을 집어넣는다.
데이터 정보로 파일명을 불러올 수도 있지만 현재 예제에서는 정말 단순하게 파일 다운로드만 살펴볼 것이기 때문에 파일명을 myFile.pdf로 지정해주었다.
다음은 Controller 부분이다.
기존 Controller를 사용해도 되긴 하지만 파일 기능 관리를 따로 하기 위해 FileController를 생성하였다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@Controller
@RequestMapping("/download")
public class FileController {
ResourceLoader resourceLoader;
@Autowired
public FileController (ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@RequestMapping("/{fileName}")
public ResponseEntity<Resource> resourceFileDownload(@PathVariable String fileName) {
try {
Resource resource = resourceLoader.getResource("file:/C:/Devroot/temp/" + fileName);
File file = resource.getFile();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM.toString())
.body(resource);
} catch (FileNotFoundException e) {
e.printStackTrace();
return ResponseEntity.badRequest().body(null);
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
ResourceLoader를 사용해 파일을 가져올 것이다.
ResourceLoader.getResource() 메서드를 사용한다. getResource() 메서드의 매개변수 문자열 값이 다운로드할 파일이 위치한 경로가 된다. 이를 Resource에 저장한다.
이후 Resource.getFile() 메서드를 사용해 파일을 가져온다.
그리고 ResponseEntity를 사용해 파일을 다운로드한다. ok() 메서드의 반환 값이 true이면 올바르게 파일이 다운로드된 것이다.
파일을 찾지 못하는 경우를 대비하여 FileNotFoundException 예외처리를 해주었고, Resource.getFile() 메서드 사용을 위해 IOException 예외처리를 해주었다. 만약 파일을 못 찾았다면 badRequest를 반환한다.
파일을 다운로드하기 위해 헤더에 파일 관련 정보를 넣어주어야 한다.
CONTENT_DISPOSITION 속성을 설정해주어야 파일 다운로드가 된다. 해당 속성을 attachment로 설정하면 파일 클릭 시 자동으로 다운로드 폴더에 파일이 다운로드된다.
file.length()를 불러와 CONTENT_LENGTH 속성 값에 넣어준다.
현재 탭에 pdf 파일 열기
헤더의 속성 값을 변경해주어 현재 탭에 바로 pdf 파일이 열리게 할 수 있다. (단, 다운로드는 되지 않는다.)
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + file.getName() + "\"")
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(file.length()))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_PDF.toString())
.body(resource);
Content-disposition 속성 값을 inline으로 해준다.
그리고 Content-type 속성 값이 APPLICATION_PDF이어야 한다.
이렇게 두 줄만 수정하면 간단하게 현재 탭에서 pdf 파일을 미리 보기 할 수 있다.
Content-length가 0인 경우 (다운로드한 파일이 열리지 않는 경우)
ResourceLoader.getResource() 메서드 사용 시 온전한 파일을 다운로드하였음에도 불구하고 파일이 열리지 않는 경우가 있다.
경로를 출력하여 확인했을 때, 경로 설정이 잘 되어 있음에도 불구하고 안될 때가 있다. 기본 경로가 classpath로 지정되어 있기 때문에 이런 문제가 발생할 수 있다.
try {
Resource resource = resourceLoader.getResource("/Devroot/temp/" + fileName);
File file = resource.getFile();
// 생략...
절대 경로를 사용하여 C드라이브 내 Devroot/temp/ 안에 있는 파일을 다운로드하기 위해 경로를 위와 같이 작성해주었다.
이렇게 하는 경우 temp 폴더 안에 있는 파일이 다운로드하여졌다고는 뜨지만 Content-length가 0으로 되어 있고, 실제 다운로드한 파일을 열었을 때 올바르지 않은 파일이라고 뜬다.
classpath 경로에 저장된 파일이 아니고, 자신이 직접 절대 경로를 지정하려면 앞에 file:/을 명시해주어야 한다.
try {
Resource resource = resourceLoader.getResource("file:/C:/Devroot/temp/" + fileName);
File file = resource.getFile();
// 생략...
해당 방법 외에도 설정 파일 수정을 통해 classpath를 직접 다른 경로로 지정해주는 방법도 있다.