본문 바로가기

Spring 3/Fileupload

Spring Fileupload example

스프링 파일 업로드/다운로드 예 ( 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>