Spring - 파일 업로드 & 다운로드
multipart란
- 웹 클라이언트가 요청을 보낼 때 http 프로토콜의 바디 부분에 데이터를 여러 부분으로 나눠서 보내는 것
- 보통 파일을 전송할 때 사용한다.
HttpServletRequest는 파일 업로드 즉 Multipart 데이터를 쉽게 처리하는 메소드를 제공하지 않는다.
-> 이로 인해 별도의 라이브러리를 사용해야하는데 대표적으로 아파치의 commons-fileupload가 있다.
Spring MVC의 파일 업로드
- commons-fileupload, commons-io 라이브러리, MultipartResolver Bean 추가
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
DispatcherServlet은 준비과정에서 "multipart/form-data"가 요청으로 올 경우 MultipartResolver를 사용
(WebMvcContextConfiguration.java에 추가)
@Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10485760); // 1024 * 1024 * 10
return multipartResolver;
}
파일 업로드시에는 form 태그에 enctype설정이 되어 있어야 하고 post 방식이어야 함
타입이 파일인 인풋 태그가 여러개 있고 네임 속성의 값이 같으면 파일이 배열 형태로 컨트롤러에 저장
<form method="post" action="/upload" enctype="multipart/form-data">
......
<input type="file" name="file">
<input type="submit">
</form>
Controller에서의 업로드 처리
- @PostMapping이 사용되야 함
- 업로드 파일이 하나일 경우 @RequestParam("file") MultipartFile file
- 업로드 파일이 여러 개일 경우 @RequestParam("file") MultipartFile[] files
- MultipartFile의 메소드를 이용해서 파일 이름, 파일 크기 등을 구하고 InputStream을 얻어 파일을 서버에 저장합니다.
Controller에서의 다운로드 처리
- 파일 다운로드와 관련된 헤더 정보를 출력
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Type", contentType);
response.setHeader("Content-Length", fileLength;
response.setHeader("Pragma", "no-cache;");
response.setHeader("Expires", "-1;");
- 파일을 읽어 HttpServletResponse의 OutputStream으로 출력
FileController 예시
package kr.or.connect.guestbook.controller;
import java.io.FileOutputStream;
import java.io.InputStream;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class FileController {
// get방식으로 요청이 올 경우 업로드 폼을 보여줍니다.
@GetMapping("/uploadform")
public String uploadform() {
return "uploadform";
}
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
System.out.println("파일 이름 : " + file.getOriginalFilename());
System.out.println("파일 크기 : " + file.getSize());
try(
// 맥일 경우
//FileOutputStream fos = new FileOutputStream("/tmp/" + file.getOriginalFilename());
// 윈도우일 경우
FileOutputStream fos = new FileOutputStream("c:/tmp/" + file.getOriginalFilename());
InputStream is = file.getInputStream();
){
int readCount = 0;
byte[] buffer = new byte[1024];
while((readCount = is.read(buffer)) != -1){
fos.write(buffer,0,readCount);
}
}catch(Exception ex){
throw new RuntimeException("file Save Error");
}
return "uploadok";
}
@GetMapping("/download")
public void download(HttpServletResponse response) {
// 직접 파일 정보를 변수에 저장해 놨지만, 이 부분이 db에서 읽어왔다고 가정한다.
String fileName = "connect.png";
String saveFileName = "c:/tmp/connect.png"; // 맥일 경우 "/tmp/connect.png" 로 수정
String contentType = "image/png";
int fileLength = 1116303; // connect.png 파일의 크기와 같아야 합니다. 파일의 크기를 꼭 확인해주세요.
//파일의 크기와 같지 않을 경우 프로그램이 멈추지 않고 계속 실행되거나, 잘못된 정보가 다운로드 될 수 있습니다.
// 위의 파일크기를 구하는 부분은 다음과 같은 방식으로 수정되는 것이 좋습니다. 코드를 간단하게 구현하기 위해서 직접 length를 입력하였습니다.
// File file = new File(saveFileName);
// long fileLength = file.length();
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Type", contentType);
response.setHeader("Content-Length", "" + fileLength);
response.setHeader("Pragma", "no-cache;");
response.setHeader("Expires", "-1;");
try(
FileInputStream fis = new FileInputStream(saveFileName);
OutputStream out = response.getOutputStream();
){
int readCount = 0;
byte[] buffer = new byte[1024];
while((readCount = fis.read(buffer)) != -1){
out.write(buffer,0,readCount);
}
}catch(Exception ex){
throw new RuntimeException("file Save Error");
}
}
}