반응형
Spring MVC는 파일 업로드 기능을 지원하기 위하여 
Commons 파일 업로드(http://jakarta.apache.org/commons/fileupload)와 
COS 파일 업로드(http://www.servlets.com/cos) 라이브러리를 지원하고 있다.
(Spring API의 org.springframework.web.multipart 패키지를 보면 두 개의 라이브러리를 지원하는 패키지가 따로 있음을 확인할 수 있다. )
 
Spring 프레임워크에서는 파일업로드에 대한 구현을 이미 다 만들어 놓은 MultipartResolver란 클래스가 있는데 여기에는 재미있는 property가 있다. uploadTempDir 이란 넘인데 이 속성명에서 풍기는 의미에서 처럼 Spring 프레임워크는 업로드 받을 임시저장소를 가지고 있다. 예를 들어 100명의 접속자가 500MB의 파일을 동시에 올린다고 생각해 보자. 서버는 이를 처리하기 위해 50GB의 메모리를 사용하게 되는데 이것은 서버에 과부하를 가져다 주게 된다. Spring 프레임워크는 이를 방지하기 위해 임시저장소를 두어 업로드된 파일에 대한 버퍼를 마련해 둔 것이다.
 
책에서는 빈 설정 파일을 다음과 같이 설정하였다.
 
 <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>
 
친절하게 업로드될 max사이즈도 정할 수 있음을 알 수 있다. 참고로 Resolver란 이름이 붙은 클래스에 대해서 우리가 직접 IoC를 적용할 필요가 없다. viewResolver에서도 확인할 수 있는데 우리는 어디에서도 직접적으로 viewResolver를 사용하고 있지 않다. 이것은 Spring 프레임워크가 처리하는 부분으로 우리가 감히 손대기에는 큰 일이 아닐까 한다.
 
Spring 프레임워크 워크북 에서는 업로드 기능에 대해서 FileUploadUtil 이란 클래스를 사용하고 있는데 사실 이 클래스는 불필요한 클래스다. 이미 Spring 프레임워크에서 기능을 제공해 주고 있는데 똑같은 기능을 만들 필요는 없을 듯하다. 그럼 그 기능을 해주고 있는 넘은 누구며 어떤 메소드냐?
 
아래와 같이 첨부된 파일을 정의하는 모델이 있다고 하자.
 
public class AttachFile {
        Integer fileId;              // 파일ID
        String  userFileName;  // 실제 파일명
        String  saveFileName;  // 경로포함된 파일명 
        String  contentType;    // content-type
        Long    fileSize;            // 파일 사이즈
 
        .......... getter/setter 생략
}
 
그리고 첨부된 파일을 속성으로 가지는 모델이 있다.
 
public class FileUploadBean {
       Integer fileUploadId;
       String  name;
       AttachFile file1;    // 파일 1
       AttachFile file2;    // 파일 2
       AttachFile file3;    // 파일 3
 
       ............ getter/setter 생략
}
 
 
MultiPartFile을 AttachFile로 변경해 주는 FilePropertyEditor이 있다.
 

public class FilePropertyEditor extends PropertyEditorSupport {
  String uploadRepository ;
  
  @Override
  public void setValue(Object value) {
    if (value instanceof MultipartFile) {
      MultipartFile multipartFile = (MultipartFile) value;

      if (multipartFile.isEmpty()) {
        super.setValue(null);
        return ;
      }

      String uploadTempDir = makeUploadTempDir() + File.separator;
      try {
        new File(uploadRepository + uploadTempDir ).mkdir();
        multipartFile.transferTo(new File(uploadRepository + uploadTempDir + multipartFile.getOriginalFilename()));
      }
      catch(IOException e) {
        throw new RuntimeException("Can't create item attach file : " + uploadRepository + " : " + e.getMessage());
      }
         
      AttachFile attachFile = new AttachFile();
      attachFile.setUserFileName(multipartFile.getOriginalFilename());
      attachFile.setSaveFileName(uploadTempDir + multipartFile.getOriginalFilename());
      attachFile.setContentType(multipartFile.getContentType());
      attachFile.setFileSize(multipartFile.getSize());
      
      super.setValue(attachFile);
    } else {
      super.setValue(value != null? value.toString().getBytes(): null);
    }
  }
  
  private String makeUploadTempDir() {
    UUID uuid = UUID.randomUUID();
    return uuid.toString();
  }

  public String getUploadRepository() {
    return uploadRepository;
  }

  public void setUploadRepository(String uploadRepository) {
    this.uploadRepository = uploadRepository;
  }
}

makeUploadTempDir() 메소드는 UUID를 이용하여 랜덤하게 폴더명을 만드는 것으로 중복된 파일명 처리를 위해 멋(?)을 부렸다.  우째튼 결론은 굵은 글씨체로 되어 있는 transferTo(String path) 메소드인데 API문서의 설명을 보면 감이 올 것이다.
 
마지막으로 살펴볼 부분이 빈 설정 파일일 것이다.
 
 <bean id="filePropertyEditor" class="caps.support.propertyeditor.FilePropertyEditor" singleton="false" >
    <property name="uploadRepository">
       <value>C:\temp\fileupload\upload\</value>
     </property>
 </bean>
 
 
책의 예제를 뒤져보면 Control 쪽에서 실제 저장소를 정의해 두었는데 FilePropertyEditor를 이해했다면 실제 저장소를 PropertyEditor에 정의하는 게 옳을 것이다. 그리고 MultipartResolver에서 정의한 임시 저장소와 실제 저장소는 다르다는 것을 다시 한번 더 확인해 두는 바이다


반응형

'Spring' 카테고리의 다른 글

Spring Transaction #2  (0) 2014.10.01
Spring Transaction #1  (0) 2014.10.01
VO, DTO, DAO  (0) 2013.07.08
domain object에 대한...  (0) 2013.07.08
Spring Java Mail  (0) 2013.07.05

+ Recent posts