分類  >  Web前端 >

WEB文件上傳之SpringMVC+ajaxfileupload使用(3)

tags:    時間:2014-05-04 12:06:09
WEB文件上傳之SpringMVC+ajaxfileupload使用(三)

 

1.  頁面使用Jquery ajaxfileupload插件的實現的基礎上(見WEB文件上傳之JQuery ajaxfileupload插件使用(二)),伺服器端結合採用springMVC來實現

 

2. 實現技術點:

  springMVC中正常處理時JSON數據的返回

  springMVC中異常統一攔截處理時JSON數據的返回

  springMVC中文件上傳進度監聽的實現

 

3. 具體實現:

UploadController.java

 

package com.test.controller;  import java.io.File; import java.util.Map;  import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;  import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.commons.CommonsMultipartFile;  import com.test.servlet.NoSupportExtensionException; import com.test.servlet.State;  @Controller @RequestMapping(value = "/mvc") public class UploadController {  	/** 日誌對象*/ 	private Log logger = LogFactory.getLog(this.getClass());  	private static final long serialVersionUID = 1L;  	/** 上傳目錄名*/ 	private static final String uploadFolderName = "uploadFiles";  	/** 允許上傳的擴展名*/ 	private static final String [] extensionPermit = {"txt", "xls", "zip"};  	@RequestMapping(value = "/upload.do", method = RequestMethod.POST) 	public @ResponseBody Map<String, Object> fileUpload(@RequestParam("file") CommonsMultipartFile file,  			HttpSession session, HttpServletRequest request, HttpServletResponse response) throws Exception{ 		logger.info("UploadController#fileUpload() start");  		//清除上次上傳進度信息 		String curProjectPath = session.getServletContext().getRealPath("/"); 		String saveDirectoryPath = curProjectPath + "/" + uploadFolderName; 		File saveDirectory = new File(saveDirectoryPath); 		logger.debug("Project real path [" + saveDirectory.getAbsolutePath() + "]");  		// 判斷文件是否存在 		if (!file.isEmpty()) { 			String fileName = file.getOriginalFilename(); 			String fileExtension = FilenameUtils.getExtension(fileName); 			if(!ArrayUtils.contains(extensionPermit, fileExtension)) { 				throw new NoSupportExtensionException("No Support extension."); 			} 			file.transferTo(new File(saveDirectory, fileName)); 		}  		logger.info("UploadController#fileUpload() end"); 		return State.OK.toMap(); 	}  } 

 

 

自定義CommonsMultipartResolver類

CustomCommonsMultipartResolver.java

 

package com.test.controller;  import java.util.List;  import javax.servlet.http.HttpServletRequest;  import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUpload; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.commons.CommonsMultipartResolver;  import com.test.servlet.FileProcessListener;  public class CustomCommonsMultipartResolver extends CommonsMultipartResolver {  	@Override 	protected MultipartParsingResult parseRequest(HttpServletRequest request) 			throws MultipartException { 		String encoding = determineEncoding(request); 		FileUpload fileUpload = prepareFileUpload(encoding);  		// 加入文件進度監聽器 (原Source上添加) start 		FileProcessListener processListener = new FileProcessListener( 				request.getSession()); 		fileUpload.setProgressListener(processListener); 		// 加入文件進度監聽器 (原Source上添加) end  		try { 			List<FileItem> fileItems = ((ServletFileUpload) fileUpload) 					.parseRequest(request); 			return parseFileItems(fileItems, encoding); 		} catch (FileUploadBase.SizeLimitExceededException ex) { 			throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), 					ex); 		} catch (FileUploadException ex) { 			throw new MultipartException( 					"Could not parse multipart servlet request", ex); 		} 	} } 

 

 

統一異常處理類

 

package com.test.controller;  import java.util.Map;  import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.core.Ordered; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView;  import com.test.servlet.NoSupportExtensionException; import com.test.servlet.State;  public class ExceptionHandler implements HandlerExceptionResolver, Ordered {  	private Log logger = LogFactory.getLog(this.getClass());  	public int getOrder() { 		return Integer.MIN_VALUE; 	}  	public ModelAndView resolveException(HttpServletRequest request, 			HttpServletResponse response, Object handler, Exception ex) { 		logger.info("ExceptionHandler#resolveException() start"); 		Map<String, Object> errorMap = null; 		if(ex instanceof NoSupportExtensionException) { 			errorMap = State.NO_SUPPORT_EXTENSION.toMap(); 		} else if(ex instanceof MaxUploadSizeExceededException){ //spring 中對apache common組件中拋出的FileSizeLimitExceededException做了轉換 			errorMap = State.OVER_FILE_LIMIT.toMap(); 		} else { 			errorMap = State.ERROR.toMap(); 		} 		//這裡牽扯到spring mvc的異常處理,這裡暫時做一個簡單處理,不做深究 		try { 			ObjectMapper objectMapper = new ObjectMapper(); 			response.setContentType("text/html;charset=UTF-8"); 			JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), JsonEncoding.UTF8); 			objectMapper.writeValue(jsonGenerator, errorMap); 		} catch(Exception e) { 			logger.error(e.getMessage(), e); 		} 		logger.info("ExceptionHandler#resolveException() end"); 		return null; 	} }

 

 

spring-context.xml配置

 

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" 	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" 	xmlns:p="http://www.springframework.org/schema/p"  	xsi:schemaLocation="http://www.springframework.org/schema/mvc  						http://www.springframework.org/schema/mvc/spring-mvc.xsd             			http://www.springframework.org/schema/beans              			http://www.springframework.org/schema/beans/spring-beans.xsd             			http://www.springframework.org/schema/context              			http://www.springframework.org/schema/context/spring-context.xsd             			http://www.springframework.org/schema/aop             			http://www.springframework.org/schema/aop/spring-aop.xsd             			http://www.springframework.org/schema/util             			http://www.springframework.org/schema/util/spring-util.xsd">  	<!-- configure the annotation scan base package --> 	<context:component-scan base-package="com.test.controller" /> 	<!-- open MVC annotation function --> 	<mvc:annotation-driven> 		<mvc:message-converters register-defaults="true">   			<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> 				<property name="supportedMediaTypes"> 					<list> 						<value>text/html;charset=UTF-8</value> 					</list> 				</property> 			</bean> 		</mvc:message-converters>   	</mvc:annotation-driven> 	<!-- define the exception interceptor--> 	<bean class="com.test.controller.ExceptionHandler" />  	<!-- define the upload config --> 	<bean id="multipartResolver" class="com.test.controller.CustomCommonsMultipartResolver">   		<property name="defaultEncoding" value="utf-8" /> 		<property name="maxUploadSize" value="31457280" /> 		<property name="maxInMemorySize" value="40960" /> 		<property name="uploadTempDir" value="tempFiles" /> 	</bean>  </beans>

 

 

web.xml配置

 

	<!-- spring context配置 --> 	<context-param> 		<param-name>contextConfigLocation</param-name> 		<param-value>classpath:spring-context.xml</param-value> 	</context-param> 	<listener> 		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 	</listener>  	<!-- 編碼過濾器配置 --> 	<filter> 		<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name> 		<url-pattern>/*</url-pattern> 	</filter-mapping>  	<!-- spring mvc 配置 --> 	<servlet> 		<servlet-name>springmvc</servlet-name> 		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 		<init-param> 			<param-name>contextAttribute</param-name> 			<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value> 		</init-param> 		<load-on-startup>1</load-on-startup> 	</servlet> 	<servlet-mapping> 		<servlet-name>springmvc</servlet-name> 		<url-pattern>*.do</url-pattern> 	</servlet-mapping> 	<servlet>

 

 

4. 總結

    實現過程中遇到的問題

   問題1:ajaxfileupload插件是通過監聽iframe的onload事件類實現文件上傳的無刷新非同步上傳,實際就是在onload觸發時獲取iframe的body體中的文本,在響應頭的content-Type為application/json的時候會出錯,chrome/firefox的情況下會出現iframe獲取出的json文本出現前後加入<pre></pre>標籤的情況,導致ajaxfileupload插件解析json數據出錯,原因應該是chrome/firefox瀏覽器處理content-Type為applicatioin/json自動做了html的轉換,所以自動加入了pre標籤。而在IE瀏覽器下,直接不支持content-Type為application/json的情況提示下載文件。

   解決:在返回json數據時將響應頭content-Type設置為text/html;charset=UTF-8,通過配置spring配置文件實現

 

<mvc:annotation-driven> 		<mvc:message-converters register-defaults="true">   			<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> 				<property name="supportedMediaTypes"> 					<list> 						<value>text/html;charset=UTF-8</value> 					</list> 				</property> 			</bean> 		</mvc:message-converters>   	</mvc:annotation-driven>

 

 

   結論:ajaxfileupload插件只支持content-Type=text/html的響應頭,進行數據返回。

 

   問題2:@ResponseBody註解只能針對正常處理時的json數據返回,異常處理時無作用

   解決: 通過自定義HandlerExceptionResolver類解決,不過這裡的解決方案不是最佳,無法根據是否使用@ResponseBody動態變化是否返回JSON數據

  

   問題3: springMVC文件上傳進度監聽器沒有默認的實現

   解決: 直接使用apache common的文件上傳進度監聽介面,並通過重寫CommonsMultipartResolver類的parseRequest方法進行和Fileupload類進行綁定

 

 Demo源碼見附件

 

推薦閱讀文章

Bookmark the permalink ,來源:互聯網