스프링 파일 업로드/다운로드 예 ( Spring Fileupload/Download example )
테스트 환경
Windows 7, JDK1.7, Tomcat 8, Eclipse luna, Spring 4.1.9, STS, Maven
사용 라이브러리
Spring Web MVC 용 기본 라이브러리 외에 Apache Commons Fileupload 라이브러리가 필요하므로 http://mvnrepository.com 에 접속하여 'Apache Commons Fileupload' 으로 검색하여 <dependency> 요소를 복사하고 pom.xml 에 추가한다
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
위와같이 라이브러리를 추가하고 프로젝트를 Refresh 하면 프로젝트의 Maven Dependencies 항목 아래에 다음과 같은 2개의 jar 파일이 설치된 것을 확인할 수 있다
commons-fileupload-1.3.2.jar
commons-io-1.3.2.jar
web.xml 편집(한글 인코딩( UTF-8 ) 필터 설정)
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
servlet-context.xml 파일에 다음 2개의 요소를 추가한다
<context:component-scan base-package="org.kdea.upload" />
...........
...........
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
UploadController.java
package org.dodream.java; import java.io.*; import javax.servlet.http.*; import org.dodream.model.UploadVO; import org.dodream.service.FileValidator; import org.springframework.beans.factory.annotation.*; import org.springframework.stereotype.*; import org.springframework.validation.*; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.*; import org.springframework.web.servlet.*; @Controller public class UploadController { @Autowired private FileValidator fileValidator; @RequestMapping(value="/upload/form", method=RequestMethod.GET) public String getUploadForm ( @ModelAttribute("uploadVO") UploadVO uploadVO ) { /*위에서 지정한 uploadVO 키는 폼이 처음 화면에 보일 때 폼의 각 필드가 request 영역에 저장된 UploadVO와 연결되어 오류가 발생하는 것을 막기 위한 것으로 POST 방식 요청을 처리하는 아래의 메소드와는 무관하다 */ return "upload/uploadForm"; } /*@ModelAttribute("uploadVO") UploadVO uploadVO 는 브라우저에서 전송되는 파라미터를 Model 객체에 uploadVO 라는 이름으로 저장한다는 것을 의미한다. Model 에 저장된 데이터는 내부에서 request 객체에 저장되므로 뷰에서 접근할 수 있게 된다 */ @RequestMapping(value="/upload/fileUpload", method=RequestMethod.POST) public ModelAndView fileUploaded( @ModelAttribute("uploadVO") UploadVO uploadVO, BindingResult result) { fileValidator.validate(uploadVO, result); if (result.hasErrors()) { return new ModelAndView("upload/uploadForm"); } // 업로드된 파일을 임의의 경로로 이동한다 MultipartFile file = uploadVO.getFile(); String fileName = file.getOriginalFilename(); InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = file.getInputStream(); File newFile = new File("D:/upload/" + fileName); if (!newFile.exists()) { newFile.createNewFile(); } outputStream = new FileOutputStream(newFile); int read = 0; byte[] bytes = new byte[1024]; while ((read = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, read); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStream.close(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } return new ModelAndView("upload/uploadResult"); } @RequestMapping("/upload/download") @ResponseBody public byte[] getImage(HttpServletResponse response, @RequestParam String filename) throws IOException { File file = new File("D:/upload/"+filename); byte[] bytes = org.springframework.util.FileCopyUtils.copyToByteArray(file); //한글은 http 헤더에 사용할 수 없기때문에 파일명은 영문으로 인코딩하여 헤더에 적용한다 String fn = new String(file.getName().getBytes(), "iso_8859_1"); response.setHeader("Content-Disposition", "attachment; filename=\"" + fn + "\""); response.setContentLength(bytes.length); response.setContentType("image/jpeg"); return bytes; } }
FileValidator.java
package org.dodream.service; import org.dodream.model.UploadVO; import org.springframework.stereotype.*; import org.springframework.validation.Errors; import org.springframework.validation.Validator; @Service("fileValidator") public class FileValidator implements Validator { public boolean supports(Class<?> arg0) { return false; } public void validate(Object obj, Errors errors) { UploadVO file = (UploadVO) obj; if (file.getFile().getSize() == 0) { errors.rejectValue("file", "UploadForm.SelectFile", "Please select a file!"); // 뒤이어 실행되는 뷰(jsp)에서는 spring 태그를 이용하여 file 키 // 에 저장된 위의 오류를 출력할 수 있다. 만약 message_ko.properties // 파일에 UploadForm.SelectFile.file 메시지가 등록되어 있다면 // 등록된 오류 메시지가 폼에 출력된다. 그렇지 않으면 디폴트로 위에 명시된 // 오류 메시지가 출력될 것이다 } } }
void rejectValue(String field,
String errorCode,
String defaultMessage)
Parameters:
field - the field name (may be null or empty String)
errorCode - error code, interpretable as a message key
defaultMessage - fallback default message
UploadVO.java
package org.dodream.model; import org.springframework.web.multipart.MultipartFile; public class UploadVO { private String desc; private MultipartFile file; public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public MultipartFile getFile() { return file; } public void setFile(MultipartFile file) { this.file = file; } }
다수개의 파일을 업로드할 때는 List<MultipartFile> files 으로 하고 업로드 폼에서는 <input type="file" name="files[0]"> 으로 하고 번호를 증가해 주면 된다. 다수개의 파일이 업로드 된 경우에는 각 파일을 지정된 장소에 옮기는 작업을 루프 안에서 반복적으로 수행하면 된다. 한가지 주의할 점은 폼 안에 <input type="file" name="files[0]"> 이 있을 때 이용자가 파일을 선택하지 않았더라도 폼을 전송하면 서버측의 List<MultipartFile> files 에 한개의 아이템으로 저장되므로 파일의 내용이 비어 있는지 검사하기 위해 MultipartFile.getSize()를 사용하여 처리해야 한다는 것이다. 비어 있는 파일을 처리하려는 경우에는 오류가 발생하기 때문이다.
다수개의 파일을 업로드할 때 사용되는 커맨드 오브젝트
package org.dodream.model; import java.util.List; import org.springframework.web.multipart.MultipartFile; public class UploadVO { private String desc; private List<MultipartFile> files; // 위의 files변수에 파일정보를 전달하기 위해서는 폼에서 다음과 같이 해야한다 // <input type="file" name="files[0]"> // <input type="file" name="files[1]"> public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public List<MultipartFile> getFiles() { return files; } public void setFiles(List<MultipartFile> files) { this.files = files; } }
WEB-INF/views/upload/uploadForm.jsp
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>파일 업로드 폼</title> </head> <body> <form:form method="post" enctype="multipart/form-data" modelAttribute="uploadVO" action="fileUpload"> 파일에 대한 설명: <form:input path="desc" type="text"/><br> 업로드할 파일 선택: <br> <form:input path="file" type="file"/> <%--uploadVO객체 안에 file 변수가 존재해야 한다 --%> <form:errors path="file" style="color:red;"/><br> <button type="submit">전 송</button> </form:form> </body> </html>
WEB-INF/views/upload/uploadResult.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>파일 업로드 결과</title> </head> <body> <h2>업로드된 파일 정보</h2> 파일명: ${uploadVO.file.originalFilename} <br> 설 명 : ${uploadVO.desc} <br> <form action="download" method="post"> <input type="hidden" name="filename" value="${filename}"><br> <button type="submit">DOWNLOAD</button><br> </form> </body> </html>