论坛首页 Java企业应用论坛

HttpServletRequestWrapper应用(二):包装文件上传请求

浏览 6208 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-10-11   最后修改:2009-10-11

应用一:HttpServletRequestWrapper结合过滤器处理中文乱码

 

应用二:包装文件上传请求

目的:通过HttpServletRequestWrapper包装文件上传请求,模块化文件上传处理,提高代码复用率,简化开发。

 

文件上传采用的是commons-fileupload文件上传组件1.2.1版(同时需要commons-io-1.4.jar)。

考虑到将来有可能扩展支持其他上传组件,所以设计了以下两个接口,分别用于抽象请求处理和文件操作。

 

文件操作抽象层:

 

/**
 * 之所以设计这个接口,是为了符合针对接口编程的设计原则,将来可以扩充支持其他上传组件的具体实现类型
 */
public interface FormFile {

    /**
     * 获得文件尺寸
     */
    public long getFileSize();
    /**
     * 获得文件的名称
     */
    public String getFileName();

    /**
     * 获得文件的字节数组形式的数据
     */
    public byte[] getFileData()
        throws FileNotFoundException, IOException;

    /**
     * 获得文件输入流
     */
    public InputStream getInputStream()
        throws FileNotFoundException, IOException;
    
    /**
     * 返回文件的类型,如“image/gif”,即“Content-Type”字段的值部分。
     */
    public String getContentType();

    /**
     * 销毁上传文件
     */
    public void destroy();
}
 

多媒体请求处理抽象层,用于解析请求,将文件参数封装成FormFile类型,保存在请求包装器中,稍后情看具体实现:

 

/**
 * 处理文件上传请求
 */
public interface MultipartRequestHandler {
    /**
	 * 解析文件上传请求
	 */
    public MultipartRequestWrapper handleRequest(HttpServletRequest request) throws ServletException;
}

 

接着来看针对commons-fileupload组件的FormFile实现。commons-fileupload组件将所有参数封装成FileItem类型,以List返回。所以这里通过包装FileItem对象来实现FormFile:

 

/**
 * 针对commons-fileupload的FormFile实现,持有FileItem对象的引用,通过FileItem的方法实现 FormFile 的方法
*/
public class CommonsUploadFormFile implements FormFile {

	FileItem fileItem;
	InputStream inputstream;
	//通过构造函数持有FileItem对象的引用
	public CommonsUploadFormFile(FileItem fileItem) {
		this.fileItem = fileItem;
	}
	
	public byte[] getFileData() throws FileNotFoundException, IOException {
		return fileItem.get();
	}

	public String getFileName() {
		return getBaseFileName(fileItem.getName());
	}

	public long getFileSize() {
		return fileItem.getSize();
	}

	public InputStream getInputStream() throws FileNotFoundException,IOException {
		return fileItem.getInputStream();
	}
	
	public String getContentType() {
		return fileItem.getContentType();
	}
	
	public void destroy() {
		fileItem.delete();
	}

	/**
	 * FileItem的getName()方法获得的是上传文件在客户机上存放的具体路径如:
	 * C:\Documents and Settings\shenlin\桌面\21534822.jpg
	 * 本方法用于取得该路径中的基本文件名称,即21534822.jpg
	 */
	protected String getBaseFileName(String filePath) {
		//按照路径构造File对象,通过File的getName()方法获得文件的名称
		String fileName = new File(filePath).getName();
		//检查是否成功获得文件名称,如果仍然你包含":","\\"和"\",那么利用字符串截取获得文件名称
		int colonIndex = fileName.indexOf(":");
		if (colonIndex == -1) {
			colonIndex = fileName.indexOf("\\\\");
		}
		int backslashIndex = fileName.lastIndexOf("\\");
		if ((colonIndex > -1) && (backslashIndex > -1)) {
			fileName = fileName.substring(backslashIndex + 1);
			System.out.println(fileName);
		}
		return fileName;
	}
}
 

针对commons-fileupload组件实现MultipartRequestHandler接口:

 

/**
 * 通过commons-fileupload解析文件上传请求,并将解析得到的参数保存在包装了当前请求MultipartRequestWrapper对象中
 */
public class CommonsUploadMultipartRequestHandler implements MultipartRequestHandler {

	/**默认最大尺寸限制*/
	public static final long DEFAULT_SIZE_MAX = 250 * 1024 * 1024;

	/** 小于指定尺寸的文件直接保存在内存中,否则保存在临时文件夹*/
	public static final int DEFAULT_SIZE_THRESHOLD = 256 * 1024;

	/**
	 * 解析文件上传请求
	 */
	public MultipartRequestWrapper handleRequest(HttpServletRequest request) throws ServletException {
		DiskFileUpload upload = new DiskFileUpload();
		// 最大尺寸限制
		upload.setSizeMax(DEFAULT_SIZE_MAX);
		// 小于指定尺寸的文件直接保存在内存中,否则保存在临时文件夹
		upload.setSizeThreshold(DEFAULT_SIZE_THRESHOLD);
		// 用于存放解析出来的请求参数
		List items = null;
		try {
			items = upload.parseRequest(request);
		} catch (DiskFileUpload.SizeLimitExceededException e) {
			throw new ServletException(e);
		} catch (FileUploadException e) {
			throw new ServletException(e);
		}
		
		MultipartRequestWrapper req  = new MultipartRequestWrapper(request);
		
		// 文本类型数据以Stirng类型添加到包装器中,文件以FormFile类型保存在包装器中
		Iterator iter = items.iterator();
		while (iter.hasNext()) {
			FileItem item = (FileItem) iter.next();
			if (item.isFormField()) {
				addTextParameter(req, item);
			} else {
				addFileParameter(req, item);
			}
		}
		return req;
	}

	/**
	 * 添加文本参数到包装器中
	 */
	protected void addTextParameter(MultipartRequestWrapper request,
			FileItem item) {
		// 得到方法用于返回表单字段元素的name属性值 Name
		String name = item.getFieldName();
		// 用于保存text的value值
		String value = null;
		String encoding = request.getCharacterEncoding();
		try {
			if (encoding == null) {
				value = item.getString("ISO-8859-1");
			} else {
				value = item.getString(encoding);
			}
		} catch (java.io.UnsupportedEncodingException uee) {
			value = item.getString();
		}
		// 添加参数到包装器
		request.setParameter(name, value);
	}

	/**
	 * 添加文件参数到包装器中
	 */
	protected void addFileParameter(MultipartRequestWrapper request,FileItem item) {
		FormFile formFile = new CommonsUploadFormFile(item);
		// 得到方法用于返回表单字段元素的name属性值 Name
		String name = item.getFieldName();
		request.setParameter(name, formFile);
	}
}
 

 

接着我们来看请求包装器的实现,由于HttpServletRequest没有提供返回文件类型参数的方法,只好自己增加两个,感觉挺不爽的,在JSP或servlet中必须将请求类型装换成MultipartRequestWrapper才可以使用:

 

/**
 * 包装HttpServletRequest,针对multipart requests 重新实现getParameter,以方便获得普通表单域参数(文本)。
 * 文件参数和
 */
public class MultipartRequestWrapper extends HttpServletRequestWrapper {
	/**
	 * 用于存放multipart request的参数
	 */
	protected Map parameters;

	/**
	 * 持有被装饰者(HttpServletRequest对象)引用,初始化用于存放multipart request的参数的Map
	 */
	public MultipartRequestWrapper(HttpServletRequest request) {
		super(request);
		this.parameters = new HashMap();
	}

	/**
	 * 保存文本参数
	 */
	public void setParameter(String name, String value) {
		String[] oldArray = (String[]) parameters.get(name);
		if (oldArray == null) {
			oldArray = new String[0];
		}
		String[] newArray = new String[oldArray.length + 1];
		System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
		newArray[oldArray.length] = value;
		parameters.put(name, newArray);
	}

	/**
	 * 保存文件参数
	 * @param name
	 * @param value
	 */
	public void setParameter(String name, FormFile value) {
		FormFile[] oldArray = (FormFile[]) parameters.get(name);
		if (oldArray == null) {
			oldArray = new FormFile[0];
		}
		FormFile[] newArray = new FormFile[oldArray.length + 1];
		System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
		newArray[oldArray.length] = value;
		parameters.put(name, newArray);
	}

	/**
	 * 通过包装器存放参数的Map对象,如果参数有多个值返回第一个值。
	 */
	public String getParameter(String name) {
		String value = null;
		String[] valueArray = (String[]) parameters.get(name);
		if ((valueArray != null) && (valueArray.length > 0)) {
			value = valueArray[0];
		}
		return value;
	}

	/**
	 *  通过包装器存放参数的Map对象获得参数,如果不为null ,以String[]形式返回。
	 */
	public String[] getParameterValues(String name) {
		String[] value = (String[]) parameters.get(name);
		return value;
	}
	
	/**
	 * HttpServletRequest没有提供返回文件的方法,自己增加一个,
	 * 在servlet或jsp中必须把请求类型转换成MultipartRequestWrapper才可以访问该方法
	 * 通过包装器存放参数的Map对象,如果参数有多个值返回第一个值。
	 */
	public FormFile getFormFile(String name) {
		FormFile value = null;
		FormFile[] valueArray = (FormFile[]) parameters.get(name);
		if ((valueArray != null) && (valueArray.length > 0)) {
			value = valueArray[0];
		}
		return value;
	}

	/**
	 * HttpServletRequest没有提供返回文件的方法,自己增加一个,
	 * 在servlet或jsp中必须把请求类型转换成MultipartRequestWrapper才可以访问该方法
	 *  通过包装器存放参数的Map对象获得参数,如果不为null ,以FormFile[]形式返回。
	 */
	public FormFile[] getFormFiles(String name) {
		FormFile[] value = (FormFile[]) parameters.get(name);
		return value;
	}
	
	/**
	 * 通过包装器存放参数的Map对象 获得参数名称枚举
	 */
	public Enumeration getParameterNames() {
		Vector list = new Vector();
		Collection multipartParams = parameters.keySet();
		Iterator iterator = multipartParams.iterator();
		while (iterator.hasNext()) {
			list.add(iterator.next());
		}
		return Collections.enumeration(list);
	}

	/**
	 *  返回存放参数的Map对象
	 */
	public Map getParameterMap() {
		return parameters;
	}
}
 

下面就该选择合适的地点来执行请求的处理了,我选择在过滤器中,比较简单。请看filter.ContentTypeFilter的doFilter()方法实现(部分代码是应用一中处理中文乱码用的):

 

public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
        //设置请求响应字符编码
		request.setCharacterEncoding(charset);
		response.setCharacterEncoding(charset);
		
        HttpServletRequest req = (HttpServletRequest)request;
	//包装请求,在getParameter方法中增加字符编码转换逻辑	
        if(req.getMethod().equalsIgnoreCase("get"))
        {
        	req = new GetHttpServletRequestWrapper(req,charset);
        }
        //处理多媒体类型的请求,并使用MultipartRequestWrapper包装请求,包装器作为请求对象传递给目标servlet或JSP
        if(req.getMethod().equalsIgnoreCase("post") && req.getHeader("Content-Type").contains("multipart/form-data"))
        {
        	req = new CommonsUploadMultipartRequestHandler().handleRequest(req);  
        }
		
		System.out.println("----请求被"+config.getFilterName()+"过滤");
		//执行下一个过滤器(如果有的话,否则执行目标servlet)
		chain.doFilter(req, response);
		
		System.out.println("----响应被"+config.getFilterName()+"过滤");

	}
 

最后请看测试servlet代码,获得文本参数直接使用ServletRequest接口定义的getParameter(name)等方法就可以了,获得文件就不得不将请求类型装换成MultipartRequestWrapper,然后调用FormFile getFormFile(String name)和FormFile[] getFormFiles (String name)方法了:

 

/**
 * 文件上传测试
 */
public class FileUpload extends HttpServlet {
	public static final String FileDIR = "/upload/";
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//request转换成MultipartRequestWrapper类型
		MultipartRequestWrapper req = (MultipartRequestWrapper)request;
		//获得上传文件的真实绝对路径
		String saveRealPath = request.getRealPath(FileDIR);
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		//打印表单文本域text1的值
		String text1 = req.getParameter("text1");
		FormFile file1 = req.getFormFile("file1");
		//保存file1
		DoUploadFile.saveFile(file1,saveRealPath);
		//根据file1保存的相对路径,构造图片标签,用于显示图片
		out.print(text1);
		out.print(":</br>");
		out.print("<img src=\"");
		out.print(request.getContextPath());
		out.print(FileDIR);
		out.print(file1.getFileName());
		out.print("\" /></br>");
		
		//打印表单文本域text2的值
		String text2 = req.getParameter("text2");
		out.print(text2);
		out.print(":</br>");
	
		//叫file2的表单文件域有两个,所以需要循环处理
		FormFile[] file2 = req.getFormFiles("file2");
		for(FormFile file:file2)
		{
			DoUploadFile.saveFile(file,saveRealPath);
			out.print("<img src=\"");
			out.print(request.getContextPath());
			out.print(FileDIR);
			out.print(file.getFileName());
			out.print("\" /></br>");
		}
		out.flush();
		out.close();
	}
}
 

文件保存工具类:

 

/**
 * 用于实现文件的上传的工具类
 */
public final class DoUploadFile {
	/**
	 * 依据传递参数上传文件
	 * @param formfile 上传的文件
	 * @param filePath  文件保存路径
	 * @throws IOException
	 */
	public static void saveFile(FormFile formfile,String filePath)
			throws IOException {
		
		String fileName = formfile.getFileName();
				
		File dir = new  File(filePath);
		if(!dir.exists()) dir.mkdir();
		
		// 获取上传文件的输入流
		InputStream in = new BufferedInputStream(formfile.getInputStream());

		// 实例化文件空壳(根据 filePath 路径名字符串和 fileName 路径名字符串创建一个新 File 实例)
		File fileSaved = new File(filePath, fileName);

		// 创建二进制文件输出流
		OutputStream os = new FileOutputStream(fileSaved);

		int bytesRead = 0;
		byte[] buffer = new byte[8192];
		while ((bytesRead = in.read(buffer, 0, 8192)) != -1) {
			os.write(buffer, 0, bytesRead);
		}
		os.close();

		// 关闭文件流
		os.close();
		in.close();
	}
}
 

到此结束,欢迎拍砖。

   发表时间:2010-07-16  
正在找的好东西   谢谢·
0 请登录后投票
   发表时间:2010-07-17  
鼓励下原创,不过估计大家应该都已经有从原来项目里提取出来的类似代码,呵呵 不过可以借鉴改善
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics