目录

SpringMVC框架中:

解决方案一:

解决方案二:

解决方案三:

SSH框架中:


SpringMVC框架中:

相信在Javaweb做文件上传的时候大家都用到commons-fileupload这个组件,使用这个组件实现文件上传时都会用到这句代码List<FileItem> list = servletFileUpload.parseRequest(request);,意思是解析从客户端发送到服务器的request请求(Form表单)得到一个FileItem的List集合,这样每个FileItem都可以简单地通过封装好的方法获得文件名,文件信息等。但是一旦处理不慎就会出现List.size()为0的问题,即解析后得不到FileItem。


琢磨了很久我才知道,之所以之前commons-fileupload能上传但后来又不能上传,是因为后来我在配置文件中注入了一个叫做CommonsMultipartResolver的组件。注入这个组件是因为在项目其他地方要用到MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;,只有注入了这个组件,request强制转换为MultipartHttpServletRequest才不会报错。

首先我们来看下Spring mvc 中文件上传的配置

<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8" />
 
		<property name="maxUploadSize" value="2000000000" />
	</bean>

这样说来,没有引入CommonsMultipartResolver前,commons-fileupload通过servletFileUpload.parseRequest(request)将request解析了,能得到FileItem集合。引入CommonsMultipartResolver之后,进行同样的解析得不到FileItem集合了。那只有一个原因,就是前后request对象变了。下面就来debug查看前后request对象的区别:


没有引入CommonsMultipartResolver前:

servlet 获取当前response servlet获取file_文件上传


引入CommonsMultipartResolver后:

servlet 获取当前response servlet获取file_javaWeb_02

servlet 获取当前response servlet获取file_List_03


对比前后可见,request对象确实不一样。那CommonsMultipartResolver究竟做了什么?


打开CommonsMultipartResolver源码:

protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
        String encoding = this.determineEncoding(request);
        FileUpload fileUpload = this.prepareFileUpload(encoding);

        try {
            List ex = ((ServletFileUpload)fileUpload).parseRequest(request);
            return this.parseFileItems(ex, encoding);
        } catch (SizeLimitExceededException var5) {
            throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), var5);
        } catch (FileUploadException var6) {
            throw new MultipartException("Could not parse multipart servlet request", var6);
        }
    }

servlet 获取当前response servlet获取file_文件上传_04

从源码可以看到:CommonsMultipartResolver本身就依赖于commons-fileupload这个包,自己也有一个叫做parseRequest的方法,这个方法调用了commons-fileupload包里FileUpload的parseRequest方法。


现在知道原因了,原来CommonsMultipartResolver这个组件会检测从客户端来的request,若是multipart/form-data数据就会自动将其用common-fileupload的parseRequest方法进行解析。因此引入这个组件后,想再通过List<FileItem> list = servletFileUpload.parseRequest(request); 解析得到FileItem是不可能的,此前已经解析过一次了,再解析当然为空。


解决方案一:

不要再使用List<FileItem> list = servletFileUpload.parseRequest(request); 这个方法得到FileItem的List(之后遍历这个List获取Form表单的数据)。 
既然CommonsMultipartResolver已经帮我们解析了,那就直接用:

if(!ServletFileUpload.isMultipartContent(request)){
                //如果上传的数据不是表单原始的数据(经过编码),则直接返回。因为只有表单原始数据才会被接收
                //http://stackoverflow.com/questions/4526273/what-does-enctype-multipart-form-data-mean
                return;
            }

            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            dynamicsText = multipartRequest.getParameter("dynamicsText");//获取表单文本部分
            MultipartFile multipartFile = multipartRequest.getFile("dynamicsFile");//获取表单

            String savePath = request.getSession().getServletContext().getRealPath("/") + "user_space/"+userId;
            File file = new File(savePath);
            if (!file.exists() && !file.isDirectory()) {
                file.mkdirs();//新建文件夹(多重)
            }
            dynamicsFile = multipartFile.getOriginalFilename();
            multipartFile.transferTo(new File(savePath+"/"+dynamicsFile));

解决方案二:

1)删除Spring MVC文件上传配置

<!-- 
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8" />
 
		<property name="maxUploadSize" value="2000000000" />
	</bean>
	 -->

在控制器里面自己完成request的解析(当然上面spring MVC提供的两种方法是不能用的,所有上传的地方都需要自己做处理)

public void upload3(HttpServletRequest request) {
		DiskFileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		try {
			List<FileItem> list = upload.parseRequest(request);
			for(FileItem item : list){
				if(item.isFormField()){
					
				}else{
					//item.write(new File(""));
				}
			}
		} catch (FileUploadException e) {
			e.printStackTrace();
		}
		
	}

解决方案三:

如果是需要使用的ProgressListener监听器我们可以重写 CommonsMultipartResolver的parseRequest方法

package com.lwp.spring.ext;
 
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.lwp.listener.FileUploadListener;
public class CommonsMultipartResolverExt extends CommonsMultipartResolver {
	@Override
	protected MultipartParsingResult parseRequest(HttpServletRequest request)
			throws MultipartException {
		FileUploadListener listener = new FileUploadListener();
		String encoding = determineEncoding(request);
		FileUpload fileUpload = prepareFileUpload(encoding);
		fileUpload.setProgressListener(listener);
		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);
		}
	}
}

监听器方法

import org.apache.commons.fileupload.ProgressListener;
 
public class FileUploadListener implements ProgressListener {
 
	@Override
	public void update(long arg0, long arg1, int arg2) {
		//arg0 已经上传多少字节
		//arg1 一共多少字节
		//arg2 正在上传第几个文件
		System.out.println(arg0 +"\t" + arg1 +"\t" + arg2);
	}
	
}

配置文件改为我们自己的(这种方式的缺陷是,所有文件上传都需要使用到Listener)

<bean id="multipartResolver"
		class="com.lwp.spring.ext.CommonsMultipartResolverExt">
		<property name="defaultEncoding" value="UTF-8" />
		<property name="maxUploadSize" value="2000000000" />
	</bean>

注: 综上所述,如果只是普通的文件上传spring MVC 完全可以完成,如果需要使用进度条的listener前段可以使用假的进度条或者是上面的两种方式.

SSH框架中:

用到组件为common-io.jar和common-fileupload.jar,JSP页面是上一个表单,然后有一个上传文件框,提交到对应的Servlet,Servlet调用API做相应的上传操作。

在Servlet中有一句API是这样的:

List<FileItem> fileItemList = upload.parseRequest(request);

意思解析表单中的每一个表单项,封装成FileItem对象,以List方式返回。刚开始怎么也上传不成功,在网上搜了一下资料,很多人说是表单没有写这句话("enctype="multipart/form-data""),但是我的表单是这样写的啊,也不成功。。于是我打印了一下fileItemList的长度,结果为0,原来原因出在这里。折腾了好久,才知道原来是Struts2的过滤器在作祟,因为我的项目里面有Struts2的环境,将web.xml配置文件做相应修改即可完成。修改如下:

<filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

改成

<filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>*.action</url-pattern>
    </filter-mapping>

问题就解决了