SpringMVC 上傳文件出現 Provisional headers are shown 和 response 數據 無法輸出問題

一、 在項目中使用springMVC 上傳文件的時候,在上傳文件的大小超過設定的值之後,異常解析器中的異常輸出信息無法輸出。

上傳文件的時候有個文件大小的限制,在超出這個限制之後.會拋出MaxUploadSizeExceededException異常,該異常是spring檢查上傳文件的信息的時候拋出的,此時還沒進入Controller內解析文件的方法中。

產生原因:tomcat7.0.67版本的bug。
解決方式:改變tomcat版本 / 新建一個springmvc 攔截器

沒超過限定大小的文件上傳成功之後返回的數據:

二、 開發環境

eclipse Mars2

apache-tomcat-7.0.67
mysql 5.5
jersey-client-1.18.jar
jersy-core-1.18.jar 使用jersey進行文件的上傳(contos 文件服務器)

三、文件配置

springmvc.xml

<!-- 加載文件上傳路徑配置文件 解決 @controller 中無法注入配置文件中的屬性 -->
<context:property-placeholder location="classpath*:file.properties"/>

<!-- 配置包掃描器 只掃描 @Controller -->
<context:component-scan base-package="com.oa,com.project,com.sso">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
</context:component-scan>

<!-- 配置註解驅動 -->
<mvc:annotation-driven/>

<!-- 視圖解析器 -->
<bean id="viewResolverCommon" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="" />
    <property name="suffix" value="" />
</bean>

<!-- 文件上傳  -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 單文件上傳大小20M-->
    <property name="maxUploadSize" value="20971520"/>
    <property name="defaultEncoding" value="UTF-8" />
</bean>

<!--  異常處理 -->
<bean id="exceptionResolver" class="com.project.common.exception.ExceptionResolver" />

ExceptionResolver

public class ExceptionResolver implements HandlerExceptionResolver{

private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionResolver.class);

@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    if(ex instanceof MaxUploadSizeExceededException){
        ObjectMapper objectMapper = new ObjectMapper();
        response.setContentType("application/json;charset=UTF-8");
        try {
            PrintWriter writer = response.getWriter();
            objectMapper.writeValue(response.getWriter(), LXResult.build(LXStatus.MAX_UPLOAD_SIZE.value(), LXStatus.MAX_UPLOAD_SIZE.display()));
            writer.flush();
        } catch (Exception ie) {
            LOGGER.error("Failed to serialize the object to json for exception handling.", ie);
        } 
        return new ModelAndView();
    } 
    return null;
}}

四、Provisional headers are shown

這個警告的意思是:請求的資源可能會被(擴展/或其他什麼機制)屏蔽掉。
爲什麼會出現這種情況?(參照:https://bz.apache.org/bugzilla/show_bug.cgi?id=57438
之所以會出現這個警告,是因爲去獲取該資源的請求其實並(還)沒有真的發生,所以 Header 裏顯示的是僞信息,直到服務器真的有響應返回,這裏的 Header 信息纔會被更新爲真實的。不過這一切也可能不會發生,因爲該請求可能會被屏蔽或者response多次重定向。

Running a Spring MVC 4.1.4 web application on JDK 7, Ubuntu 14.10 (and Debian Wheezy).
Tested on version 7.0.53 through 7.0.57. The web application works as expected in versions 7.0.53 and 7.0.54. Version 7.0.55 and up display the following issue.

When uploading a large file, larger than the specified maximum, the exception org.springframework.web.multipart.MaxUploadSizeExceededException (caused by a org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException) is caught via an @ExceptionHandler. After logging the exception and resolving some parameters from the inputstream a redirect is performed like this:

return new org.springframework.web.servlet.ModelAndView(“redirect:/error”, new org.springframework.ui.ExtendedModelMap());

I expect that this would generate a HTTP 302 redirect and will trigger a GET request on “/error”. The upload obviously is a POST request and the redirect would perform a GET request. Tomcat version 7.0.54 and .53 indeed do that, but in version 55 and up it remains a POST request. The complete multipart content-type is saved and the whole request is repeated until the server throws an error: (failed) net::ERR_CONNECTION_RESET.

Provisional headers:
Request URL:https://localhost:8443/import/ratings
Request Headers
Provisional headers are shown
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Content-Type:multipart/form-data; boundary=—-WebKitFormBoundaryNT3MUmtXDpFSSCLy
Origin:https://localhost:8443
Referer:https://localhost:8443/import/ratings
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Request Payload
——WebKitFormBoundaryNT3MUmtXDpFSSCLy
Content-Disposition: form-data; name=”commandName”

importRatingsCommand
——WebKitFormBoundaryNT3MUmtXDpFSSCLy
Content-Disposition: form-data; name=”ratings”; filename=”file.pdf”
Content-Type: application/pdf

這是tomcat版本造成的bug.使得MaxUploadSizeExceededException進行死循環。就是302的重複重定向。

五。既然已經知道問題的產生原因。那麼就有以下兩種解決方案

5.1 簡單粗暴–直接更換tomcat版本(不更改異常解析器和springmvc配置)

apache-tomcat-7.0.67 (不可用)
apache-tomcat-7.0.39 (可用)
apache-tomcat-7.0.70 (可用)
其他版本沒有測試,不知道是否存在此bug。

springmvc.xml

!-- 文件上傳  -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 單文件上傳大小20M-->
    <property name="maxUploadSize" value="20971520"/>
    <property name="defaultEncoding" value="UTF-8" />
</bean>

<!--  異常處理 -->
<bean id="exceptionResolver" class="com.project.common.exception.ExceptionResolver" />

異常解析類ExceptionResolver和前面的保持一致(由於前後端數據使用json交互,故返回的ModelAndView 爲null,不需要視圖的跳轉)。

5.2 退而求其次–使用springMVC攔截器(必須把maxUploadSize 設置大一點,讓它不會拋異常出來)

新建一個攔截器專門用來控制上傳大小,輸出異常之後需要json輸出的數據。(如果不是json交互的,可以直接拋出異常或者處理,都是可以的)

springmvc.xml

<!--多文件上傳部分 (不需要配置 異常解析器)-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 單文件上傳大小20M ,這裏需要設置的大一點,避免在攔截器之前出現異常,很重要,這裏是400M-->
    <!--maxUploadSize 使用攔截器時可以不用設置-->
    <property name="maxUploadSize" value="409715200"/>
    <property name="defaultEncoding" value="UTF-8" />
</bean> 

<!--註冊攔截器 方式一(上傳文件的大小寫在配置文件中) 推薦-->
<mvc:interceptors>  
    <mvc:interceptor>
        <!--此處只攔截文件上傳的請求-->
        <mvc:mapping path="/file/fileUpload.do"/> 
        <bean class="com.sso.interceptor.FileInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

<!--註冊攔截器 方式二(上傳文件的大小在這裏注入進來)不推薦-->
<!--<mvc:interceptors>  
    <mvc:interceptor>
        <!--此處只攔截文件上傳的請求-->
        <mvc:mapping path="/file/fileUpload.do"/> 
        <bean class="com.sso.interceptor.FileInterceptor">
            <property name="MAX_UPLOAD_SIZE" value="20971520"/>
        </bean>
    </mvc:interceptor>
</mvc:interceptors>-->

FileInterceptor.java

/**
 * @ClassName: FileInterceptor 
 * @Description: 上傳附件大小攔截器
 * @author liudongdong
 * @date 2016年8月17日 下午2:47:39 
 * @version V1.0
 */
public class FileInterceptor implements HandlerInterceptor{

//配置文件中讀取上傳文件的大小(20M)[方式一] 可以使用其他方式
@Value("${MAX_UPLOAD_SIZE}")
private long MAX_UPLOAD_SIZE;

/**
 *//攔截器使用方式二時,這裏使用set方法
 *private long MAX_UPLOAD_SIZE;
 *
 *public void setMAX_UPLOAD_SIZE(long mAX_UPLOAD_SIZE) {
 *  thisMAX_UPLOAD_SIZE = mAX_UPLOAD_SIZE;
 *}
 */

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    //判斷是否是多文件上傳的請求
    if (request != null && ServletFileUpload.isMultipartContent(request)) {
        ServletRequestContext requestContext = new ServletRequestContext(request);
        long requsetSize = requestContext.getContentLength();
        if (requsetSize > MAX_UPLOAD_SIZE) {
            ObjectMapper mapper = new ObjectMapper();
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            PrintWriter writer = response.getWriter();
            //json輸出提示信息
            mapper.writeValue(writer, LXResult.build(LXStatus.MAX_UPLOAD_SIZE.value(), LXStatus.MAX_UPLOAD_SIZE.display()));
            writer.flush();
            return false;
        }
    }
    return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv)
        throws Exception {
    // TODO Auto-generated method stub
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
    }
}

測試成功(存在bug的tomcat7.0.67,使用攔截器)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章