Spring MVC - 파일 업로드

2010. 9. 3. 22:09plming/Java - Spring

1. 파일 업로드를 위한 기본 설정

Spring MVC는 파일 업로드 기능을 지원하기 위하여 Commons 파일 업로드(http://jakarta.apache.org/commons/fileupload)와 COS 파일 업로드(http://www.servlets.com/cos) 라이브러리를 지원하고 있다.

파일 업로드 기능을 구현하기 위해서는 먼저 빈 설정파일에서 MultipartResolver를 정의해야 한다.

 action-servlet.xml - 파일 업로드 기능을 구현하기 위하여 MultipartResolver 정의

 <bean id="multipartResolver"
 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
     <property name="maxUploadSize">
         <value>10000000</value>
     </property>
     <property name="uploadTempDir" ref="uploadDirResource" />
 </bean>

 <bean id="uploadDirResource" class="org.springframework.core.io.FileSystemResource">
     <constructor-arg>
         <value>D:/Temp/fileupload/temp/</value>
     </constructor-arg>
 </bean>


만약 COS 파일 업로드 라이브러리를 이용하고자 한다면 "org.springframework.web.multipart.cos.CosMultipartResolver"를 이용하면 된다.


2. 가장 간단한 구현 방법 - 첨부 파일의 수가 정적, 동적인 경우 모두 이용 가능

Spring MVC에서 파일 업로드 기능을 구현하기 위하여 가장 쉬운 방법은 HttpServletRequest를 MultipartHttpServletRequest로 캐스팅(Casting)한 다음 첨부된 파일을 직접 얻어내는 방법이다.

 edit.jsp - 파일 업로드 부분

 <form name="boardForm" action="${pageContext.request.contextPath}/board/editBoard.do"
     method="post" enctype="multipart/form-data">

 <input type="file" style="width:400" name="file1"/>
 <input type="file" style="width:400" name="file2"/>
 <input type="file" style="width:400" name="file3"/>

 

 BoardFormController.java - 파일 업로드 부분

 public class BoardFormController extends SimpleFormController {

     private List getBoardFileList(HttpServletRequest request) {
         MultipartHttpServletRequest mpRequest = (MultipartHttpServletRequest) request;
         Iterator fileNameIterator = mpRequest.getFileNames();

         List boardFileList = new ArrayList();
         while (fileNameIterator.hasNext()) {
             MultipartFile multiFile = mpRequest
                     .getFile((String) fileNameIterator.next());

             if (multiFile.getSize() > 0) {
                 BoardFile boardFile = FileUploadUtil.uploadFormFile(multiFile,
                         realUploadPath);
                 boardFileList.add(boardFile);
             }
         }

         return boardFileList;
     }
 }

 

 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;

 import net.javajigi.board.model.BoardFile;

 import org.springframework.web.multipart.MultipartFile;

 public class FileUploadUtil {

     public static BoardFile uploadFormFile(MultipartFile formFile, String realPath) {
         InputStream stream;

         UUID uuid = UUID.randomUUID();
         String tempFileName = uuid.toString();

         try {
             stream = formFile.getInputStream();

             OutputStream bos = new FileOutputStream(realPath + tempFileName);
             int bytesRead = 0;
             byte[] buffer = new byte[8192];
             while ((bytesRead = stream.read(buffer, 0, 8192)) != -1) {
                 bos.write(buffer, 0, bytesRead);
             }
             bos.close();
             stream.close();

             if (logger.isDebugEnabled()) {
                 logger.debug("The file has been written to \"" + realPath
                         + tempFileName);
             }
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         } catch (IOException e) {
             e.printStackTrace();
         }

         BoardFile boardFile = new BoardFile();
         boardFile.setFileName(formFile.getOriginalFilename());
         boardFile.setFileSize(formFile.getSize());
         boardFile.setContentType(formFile.getContentType());
         boardFile.setTempFileName(tempFileName);

         return boardFile;
     }
 }



3. PropertyEditor를 이용하는 방법 - 첨부 파일의 수가 정적일 경우 사용.

자바빈 규약에는 자바빈에 전달되는 속성의 타입(Type)을 개발자들이 지정하는 것을 가능하도록 하기 위하여 PropertyEditor라는 기능을 지원하고 있다.  Spring MVC의 MultipartResolver를 이용하여 파일을 업로드한 다음 데이터 바인딩(Data Binding)을 이용하여 데이터를 전달할 경우 Command의 속성에 전달되는 데이터의 타입은 MultipartFile이다.  그런데 자료실 게시판에서 첨부 파일 정보를 담당하고 있는 도메인 모델은 BoardFile이다.  따라서 데이터 바인딩 할 때 첨부파일 정보를 MultipartFile에서 BoardFile로 변경해주어야 한다.  이와 같은 작업이 가능하도록 지원하는 것이 자바빈의 PropertyEditor이다.

 public class Board extends BaseObject {
     private BoardFile file1 = null;
     private BoardFile file2 = null;
     private BoardFile file3 = null;

     public BoardFile getFile1() {
         return file1;
     }
     public void setFile1(BoardFile file1) {
         this.file1 = file1;
     }
     ::
 }

 

 BoardFileMultipartPropertyEditor.java - MultipartFile을 BoardFile로 변경해주는 PropertyEditor

 public class BoardFileMultipartPropertyEditor extends PropertyEditorSupport {
     private String realUploadPath;

     public BoardFileMultipartPropertyEditor(String realUploadPath) {
         this.realUploadPath = realUploadPath;
     }

     public void setValue(Object value) {
         if (value instanceof MultipartFile) {
             MultipartFile multipartFile = (MultipartFile) value;

             if (multipartFile.getSize() > 0) {
                 BoardFile boardFile = FileUploadUtil.uploadFormFile(
                         multipartFile, realUploadPath);
                 super.setValue(boardFile);
             } else {
                 super.setValue(null);
             }
         } else {
             super.setValue(value != null ? value.toString().getBytes() : null);
         }
     }
 }

 

 데이터 바인딩을 위하여 BoardFileMultipartPropertyEditor를 이용하는 BoardFormController

 public class BoardFormController extends SimpleFormController {

     protected void initBinder(HttpServletRequest request,
             ServletRequestDataBinder binder) throws Exception {
         binder.registerCustomEditor(BoardFile.class,
                 new BoardFileMultipartPropertyEditor(realUploadPath));

     }

     protected ModelAndView onSubmit(HttpServletRequest request,
             HttpServletResponse response, Object command,
             BindException exception) throws Exception {
         Board board = (Board) command;
         board.addBoardFile(board.getFile1());
         board.addBoardFile(board.getFile2());
         board.addBoardFile(board.getFile3());

         return new ModelAndView(getSuccessView());
     }
 }


  PropertyEditor를 이용할 경우 Command클래스나 Controller가 MultipartFile에 종속될 필요없이 데이터 바인딩만으로 파일업로드 기능을 구현하는 것이 가능하지만, 첨부파일의 수가 동적으로 변경되는 경우에는 사용하기 힘들다는 단점이 있다.

  PropertyEditor는 자바빈의 데이터 바인딩을 처리하기 위하여 유용하게 사용할 수 있는 기능이다.  Spring 프레임워크에서도 이미 많은 PropertyEditor를 제공하고 있다.  PropertyEditor에 관한 자세한 내용은 http://www.springframework.org/docs/reference/validation.html#beans-beans-conversion 문서를 참고하기 바란다.


4. MultipartFile 리스트를 이용하는 방법 - 첨부 파일의 수가 동적일 경우 사용

 edit.jsp - 파일 업로드 기능

 <form name="boardForm" action="${pageContext.request.contextPath}/board/editBoard.do"
     method="post" enctype="multipart/form-data">

 <input type="file" style="width:400" name="file[0]"/>
 <input type="file" style="width:400" name="file[1]"/>
 <input type="file" style="width:400" name="file[2]"/>

 

 public class Board extends BaseObject {
     private List file = null;

     public Board() {
         boardFiles = new ArrayList();
         file = new ArrayList();
     }

     public List getFile() {
         return file;
     }

     public void setFile(List file) {
         this.file = file;
     }
 }

 

 public class BoardFormController extends SimpleFormController {
     protected ModelAndView onSubmit(HttpServletRequest request,
             HttpServletResponse response, Object command,
             BindException exception) throws Exception {
         Board board = (Board) command;
         List fileList = board.getFile();
         Iterator fileIter = fileList.iterator();
         while (fileIter.hasNext()) {
             MultipartFile multiFile = (MultipartFile) fileIter.next();
             if (multiFile.getSize() > 0) {
                 board.addBoardFile(FileUploadUtil.uploadFormFile(multiFile,
                         realUploadPath));
             }
         }

         return new ModelAndView(getSuccessView());
     }
 }


이 방법을 이용할 경우 MultipartFile API와 의존관계에 놓이게 되지만 첨부파일의 수가 동적으로 변경될 경우에는 유용하게 사용할 수 있다.


[출처] http://www.javajigi.net/pages/viewpage.action?pageId=28377096
[출처] Spring 프레임워크 워크북