13.8. Spring’s multipart (fileupload) support

13.8.1. Introduction

MultipartResolver 인터페이스를 사용하여 파일 업로드를 구현할 수 있습니다. 내부 구현체는 Commons FileUpload 와 COS FileUpload 를 사용합니다.

기본적으로 Spring은 Multipart 를 지원하진 않지만 Multipart를 포함하는 요청이 들어오면 Context에 설정해둔 MutipartResoolver를 사용하여 적절하게 처리할 수 있습니다.

13.8.2. Using the MultipartResolver

Multipart 기능을 사용하기 위해 먼저 사요할 MultipartResolver를 bean으로 등록해야 합니다.

CommonsMultipartResolver 등록하기 :: commons-fileupload.jar, commons-io.jar 필요함

<bean id=”multipartResolver”
    class=”org.springframework.web.multipart.commons.CommonsMultipartResolver”>

    <!– one of the properties available; the maximum file size in bytes –>
    <property name=”maxUploadSize” value=”100000″/>
</bean>

CosMultipartResolver 등록하기 :: cos.jar 필요함

<bean id=”multipartResolver” class=”org.springframework.web.multipart.cos.CosMultipartResolver”>

    <!– one of the properties available; the maximum file size in bytes –>
    <property name=”maxUploadSize” value=”100000″/>
</bean>

13.8.3. Handling a file upload in a form

화면에서 파일 입력 받기.

<html>
    <head>
        <title>Upload a file please</title>
    </head>
    <body>
        <h1>Please upload a file</h1>
        <form method=”post” action=”upload.form” enctype=”multipart/form-data”>
            <input type=”file” name=”file”/>
            <input type=”submit”/>
        </form>
    </body>
</html>

화면에서 입력받은 파일을 객체로 바인딩 해야 하는데 이 때 String 타입으로 바인딩하려면 StringMultipartEditor, byte타입의 배열로 바인딩 하려면 ByteArrayMultipartEditor 를 사용할 수 있습니다.

byte 타입의 배열 이나 String으로 바인딩하지 않고 MultipartFile 타입으로 받으려면 별다른 바인딩이 필요 없습니다.

이 세가지 방법 중 String으로 바인딩하는 예제를 보겠습니다.

public class FileUploadController extends SimpleFormController {

    protected ModelAndView onSubmit(
        HttpServletRequest request,
        HttpServletResponse response,
        Object command,
        BindException errors) throws ServletException, IOException {

         // cast the bean
        FileUploadBean bean = (FileUploadBean) command;

         let’s see if there’s content there
        String file = bean.getFile();
        if (file == null) {
             // hmm, that’s strange, the user did not upload anything
        }

         // well, let’s do nothing with the bean for now and return
        return super.onSubmit(request, response, command, errors);
    }

    protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
        throws ServletException {
        // to actually be able to convert Multipart instance to a String
        // we have to register a custom editor
        binder.registerCustomEditor(String.class, new StringMultipartFileEditor());
        // now Spring knows how to handle multipart object and convert them
    }

}

public class FileUploadBean {

    private String file;

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

    public String getFile() {
        return file;
    }
}

오호.. 간단하군요. 집에가서 써먹어 봐야겠습니다.

13.7. Using themes

13.7.1. Introduction

테마를 적용하기 위해서는 전역적으로 사용할 스타일 시트(CSS)와 그림 파일등의 자원을 정의하는 것이 필요합니다.

13.7.2. Defining themes

ThemeSource 를 사용하면 되는데 XmlWebApplicationContext 가 ThemeSource 를 구현하고 있으며 내부 구현체로는 ResourceBundleThemeSource 를 기본으로 사용하고 있습니다.

사용자 삽입 이미지
ResourceBundleThemeSource는 classpath에 들어있는 프로퍼티 파일을 읽어 들여서 theme을 설정할 때 사용합니다.

styleSheet=/themes/cool/style.css
background=/themes/cool/img/coolBg.jpg

위와 같이 프로퍼티 파일에 설정해 두고 view에서는 spring:theme 태그를 사용하여 key에 해당하는 이름으로 각 요소들을 사용할 수 있습니다.

<%@ taglib prefix=”spring” uri=”http://www.springframework.org/tags”%>
<html>
   <head>
      <link rel=”stylesheet” href=”<spring:theme code=”styleSheet”/>” type=”text/css”/>
   </head>
   <body background=”<spring:theme code=”background”/>”>
      …
   </body>
</html>


13.7.3. Theme resolvers

마지막으로 저렇게 만들어둔 여러개의 Theme 중에 어떤 Theme를 사용할지 선택해야 합니다. 이때 ThemeResolver를 사용하면 됩니다.

Class

Description

FixedThemeResolver

defaultThemeName
속성을 사용해서 고정적인 테마를 설정합니다.

SessionThemeResolver

각 세션에 테마에 대한 정보를 유지합니다. 세션 간에 공유되지는
않습니다.

CookieThemeResolver

선택된 테마에 대한 정보를 쿠키에 유지 합니다.

13.6. Using locales

Request가 들어오면 Dispatcher는 Locale resolver를 찾게 되고 만약에 있으면 Locale을 설정할 때 사용합니다. RequestContext.getLocale() 메소드를 사용해서 Locale resolver에 의해 설정 된 Locale을 가져올 수 있습니다.

13.6.1. AcceptHeaderLocaleResolver

사용자의 브라우져에서 보내진 request의 헤더에 accept-language 부분에서 Locale을 읽어들입니다. 사용자의 OS locale을 나타냅니다.

13.6.2. CookieLocaleResolver

사용자 컴터에 쿠키를 남아 있다면 그 쿠키에 설정한 Locale을 읽어 들입니다.
다음과 같은 속성을 설정할 수 있습니다.

Property

Default

Description

cookieName

classname +
LOCALE

쿠키 이름

cookieMaxAge

Integer.MAX_INT

쿠키 살려둘 시간.

-1로 해두면 브라우저를 닫을 때 없어집니다.

cookiePath

/

Path를 지정해
주면 해당 하는 path와 그 하위 path에서만 참조할
수 있습니다.

13.6.3. SessionLocaleResolver

requst가 가지고 있는 session으로 부터 locale 정보를 가져옵니다.

13.6.4. LocaleChangeInterceptor

HandlerMapping에 인터셉터를 등록하여 특정 locale의 요청을 가로채서 특정 파라미터에 넘어 온 값으로 locale 을 알아낼 수 있습니다.

<bean id=”localeChangeInterceptor”
      class=”org.springframework.web.servlet.i18n.LocaleChangeInterceptor”>
    <property name=”paramName” value=”siteLanguage”/>
</bean>

<bean id=”localeResolver”
      class=”org.springframework.web.servlet.i18n.CookieLocaleResolver”/>

<bean id=”urlMapping”
      class=”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping”>
    <property name=”interceptors”>
        <list>
            <ref bean=”localeChangeInterceptor”/>
        </list>
    </property>
    <property name=”mappings”>
        <value>/**/*.view=someController</value>
    </property>
</bean>

여기서는 모든 .view로 끝나는 요청을 가로채서 siteLanguage라는 request의 파라미터를 조사 하여 locale을 알 수 있게 됩니다.

13.5.2. Chaining ViewResolvers

다수의 ViewResolver들을 등록해 둘 수 있습니다. 그 때 특정 view 이름을 해석할 View Resolver들을 순서대로 나열하는 것을 Chaining ViewResolver라고 하는 것 같습니다. ViewResolver들의 순서는 Ordered 인터페이스를 사용하여 설정할 수 있습니다.

레퍼런스의 예를 보겠습니다.

<bean id=”jspViewResolver” class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
  <property name=”viewClass” value=”org.springframework.web.servlet.view.JstlView”/>
  <property name=”prefix” value=”/WEB-INF/jsp/”/>
  <property name=”suffix” value=”.jsp”/>
</bean>

<bean id=”excelViewResolver” class=”org.springframework.web.servlet.view.XmlViewResolver”>
  <property name=”order” value=”1″/>
  <property name=”location” value=”/WEB-INF/views.xml”/>
</bean>

<!– in views.xml –>

<beans>
  <bean name=”report” class=”org.springframework.example.ReportExcelView”/>
</beans>

이렇게 등록 해두면 InternalResourceViewResolver는 특성상 ViewReslove 체인의 맨 마지막에 위치 하기 때문에 굳이 ExcelViewResolver에 order 속성에 숫자를 주지 않아도 되겠죠.

만약에 해당 ViewResolver에서 적당한 view를 찾지 못하면 그 다음 ViewResolver를 사용하여 찾게 됩니다. 끝까지 못찾으면 Exception이 발생합니다.

InternalResouceViewResolver와 VelocityViewResolver, FreeMarkerViewResolver 들은 맨 뒤에 위치 해야 합니다.

13.5.1. Resolving views – the ViewResolver interface

Spring이 제공하는 ViewResolver들은 다음과 같습니다.

ViewResolver

Description

AbstractCachingViewResolver

View 들을 cashing하는 기능을 제공합니다.

XmlViewResolver

ViewResolver 의 구현체로 XML파일을 사용합니다.

/WEB-INF/views.xml 을 기본 설정파일로 사용합니다.

ResourceBundleViewResolver

ViewResolver
구현체로 리소스 파일을 사용합니다.

views.properties
를 기본 리소스 파일로 사용합니다.

UrlBasedViewResolver

ViewResolver 의 구현체로 특별한 맵핑 정보 없이 의미상 view 이름을 URL로 사용합니다.

View 이름과 실제 view 자원과의
이름이 같을 때 사용할 수 있습니다.

InternalResourceViewResolver

UrlBasedViewResolver
를 상속 받았으며   InternalResourceView(Servlet,
JSP)
를 사용할 수 있습니다.

VelocityViewResolver /

FreeMarkerViewResolver

UrlBasedViewResolver 를 상속 받았으며 VelocityView FreeMarkerView를 사용할 수
있습니다.

사용하는 View 기술에 따라 적절한 ViewResolver를 선택해야겠습니다.
JSP를 사용하려면 UrlBasefViewResolver를 사용해도 되고 그 하위에 있는 InternalResourceVIewResolver를 사용할 수 있겠습니다. 하지만 JSTL을 사용하려면 InternalResourceVIewResolver를 사용해야겠네요. 🙂

JSP 사용할 때

<bean id=”viewResolver”
      class=”org.springframework.web.servlet.view.UrlBasedViewResolver”>
    <property name=”prefix” value=”/WEB-INF/jsp/”/>
    <property name=”suffix” value=”.jsp”/>
</bean>

JSP를 JSTL와 함께 사용할 때

<bean id=”jspViewResolver” class=”org.springframework.web.servlet.view.InternalResourceViewResolver”>
  <property name=”viewClass” value=”org.springframework.web.servlet.view.JstlView”/>
  <property name=”prefix” value=”/WEB-INF/jsp/”/>
  <property name=”suffix” value=”.jsp”/>
</bean>

여러 view 기술들을 사용할 때

<bean id=”viewResolver”
      class=”org.springframework.web.servlet.view.ResourceBundleViewResolver”>
    <property name=”basename” value=”views”/>
    <property name=”defaultParentView” value=”parentView”/>
</bean>

basename에 설정한 값의 ResourceBundle을 찾아서 각각의 view를 [viewname].class 는 뷰 기술, [viewname].url 은 실제 뷰 URL로 정의 해둔 프로퍼티 파일을 사용하여 찾아 줍니다.

ViewResolver들의 상위에 있는 AbstractCachingViewResolver 이 클레스가 캐슁 기능을 제공하기 때문에 이 클레스의 하위 클레스들은 엄청난 성능 향상을 맛볼 수 있습니다.
캐슁 기능을 끄고 싶을 때는 cache 속성을 false로 하면 됩니다.
런타임시 특정 view를 다시 읽어야 한다면 removeFromCache(String viewName, Locale loc) 메소드를 사용할 수 있습니다.
사용자 삽입 이미지