`
cheng888qi
  • 浏览: 282629 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

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源码见附件

 

4
2
分享到:
评论
1 楼 huazaizhou 2015-07-10  
[size=x-large][size=x-small][size=small]楼主用的spring的哪个版本呢?[/size][/size][/size]

相关推荐

Global site tag (gtag.js) - Google Analytics