`
whistler
  • 浏览: 25187 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

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

    博客分类:
  • java
阅读更多

应用一: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();
	}
}
 

到此结束,欢迎拍砖。

分享到:
评论
2 楼 mib168 2010-07-17  
鼓励下原创,不过估计大家应该都已经有从原来项目里提取出来的类似代码,呵呵 不过可以借鉴改善
1 楼 skcmm 2010-07-16  
正在找的好东西   谢谢·

相关推荐

    使用HttpServletRequestWrapper在filter修改request参数

    `HttpServletRequestWrapper` 是Servlet API中的一个抽象类,它是`HttpServletRequest` 的包装类,可以用来扩展或修改原始请求对象的功能。 标题“使用HttpServletRequestWrapper在filter修改request参数”揭示了...

    HttpServletRequestWrapper 用法

    `HttpServletRequestWrapper`是Java Servlet API中的一个类,它允许我们对`HttpServletRequest`对象进行包装,以便在请求处理过程中添加自定义的行为或者修改默认的行为。这个类位于`javax.servlet.http`包下,是`...

    HttpServletRequestWrapper

    这是一个关于HttpServletRequestWrapper使用的列子,工作需要,所以传上来的。

    AJAX+JSP上传大文件.doc

    `UploadMultipartFilter`实现了`Filter`接口,当接收到多部分请求(即包含文件上传的POST请求)时,会创建一个`UploadMultipartRequestWrapper`对象来包装原始请求。`FileUpload`库(Jakarta-Commons)用于解析HTTP...

    java修改请求参数

    在`doFilter()`方法中,可以读取原始请求参数,进行修改,然后使用`HttpServletRequestWrapper`包装修改后的参数,再继续请求链。 3. **处理逻辑内修改**:如果修改参数的逻辑比较单一,也可以选择在具体处理请求的...

    jspSmartUpload.jar

    总结起来,JSP SmartUpload.jar是解决Web应用文件上传问题的强大工具,而配合SQLServer2000的JDBC驱动,使得文件上传与数据库管理紧密结合,实现了文件的高效存储和管理。在实际项目中,开发者应充分理解和掌握这些...

    filter对request请求拦截,对请求参数进行修改

    对request请求进行拦截,对请求参数修改。常用于前台提交表单参数关键字的过滤。此工具可以对参数拦截并转义后提交到对应的处理类。 除了添加两个JsFilter.java和GetHttpServletRequestWrapper.java之外,需要在web....

    jspsmartupload上传下载

    JSpsmartupload是一款基于Java的文件上传和下载组件,它为JSP应用提供了简单易用的接口来处理文件上传操作。在本篇中,我们将深入探讨JSpsmartupload的工作原理、实现方式以及如何自定义`Request`以满足特定需求。 ...

    springboot 解决InputStream只能读取一次的问题

    这样,我们就成功地解决了Spring Boot中`InputStream`只能读取一次的问题,使得我们可以多次读取并处理请求数据,无论是用于文件上传、消息解析还是其他需要多次读取的场景。在实际项目中,根据具体需求,可能还需要...

    java filter打印请求返回参数

    1. **对`HttpServletRequest`进行封装**:通过创建`HttpServletRequestWrapper`子类来包装原始请求对象,并重写`getReader()`和`getInputStream()`方法来读取请求体数据。 2. **对`HttpServletResponse`进行封装**...

    cas单点登录(tomcat)

    - **Cas HttpServletRequestWrapper**:包装原始的HttpServletRequest对象,以便在请求中包含CAS信息。 - **TicketGrantingTicketCookieGenerator**:用于在用户浏览器中设置和读取Ticket Granting Ticket(TGT)的...

    scwcd考试中常用的API

    - **class: HttpServletRequestWrapper**:用于包装HttpServletRequest对象,允许修改请求属性。 #### RESPONSE - **interface: ServletResponse**:定义了创建响应的基本方法。 - **interface: ...

    Webwork 实现文件上传下载代码详解

    WebWork 是一个基于 Java 的轻量级Web应用框架,它为开发者提供了一种优雅的方式来处理Web请求和响应,包括文件上传和下载的功能。在WebWork中,文件上传和下载是通过拦截器(Interceptor)机制来实现的,这使得...

    struts1.x 上传下载

    最常用的是`MultipartRequestHandler`,它实现了Servlet API中的`HttpServletRequestWrapper`接口,允许处理multipart/form-data类型的请求,这是HTTP协议中用于上传文件的标准格式。 1. **配置**: 首先,需要在`...

    Killtest 免费提供 310-084 最新资料下载

    根据给定的文件信息,我们可以提炼出与IT行业特别是与SUN认证相关的多个知识点,具体涉及310-084考试的内容,该考试主要针对Sun Certified Web Component Developer for Java EE 5 Upgrade的认证。 ### 知识点一:...

    struts的request包装替换的思想

    然而,出于一些特定需求,例如日志记录、安全性增强或者性能优化,开发者可能希望对请求进行包装,即创建一个新的类来包裹原始的HttpServletRequest对象,以便在处理请求的过程中添加额外的功能。这种包装替换的思想...

    在Spring MVC或Spring Boot中使用Filter打印请求参数问题

    // 返回一个包装了请求体的ServletInputStream } // ...其他需要覆盖的方法 } ``` 通过这种方式,我们可以确保在Filter中正确地处理JSON或其他类型的请求体,同时避免因流被关闭而导致的异常。记住,处理HTTP...

    在使用实现过滤器进行request包装,获取内层request的分析

    这时,我们可以使用`HttpServletRequestWrapper`类来包装请求,并在需要时获取内层的原始请求。下面是一个简单的例子: ```java public class OriginalRequestWrapper extends HttpServletRequestWrapper { ...

    springmvc4配置防止XSS攻击的方法

    在本例中,通过检查请求头的contentType是否以"multipart"开头来判断请求是否为上传,如果为上传则不对请求内容进行XSS过滤。这是因为文件上传通常包含特殊字符,如果进行过滤,可能会影响文件的正常处理。 总结来...

Global site tag (gtag.js) - Google Analytics