`
alfred.w
  • 浏览: 92262 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

struts2 使用 jakarta 上传文件时commons fileupload的异常捕捉

阅读更多

问题:

        struts2 使用jakarta 上传文件时,如果上传文件的大小超出commons fileupload(jakarta上传文件还是依赖commons-fileupload)设置的大小就会在进入action以前抛出异常.
        如果想返回用户的输入界面(input),那么页面原来的参数会丢失。

首先看一下struts2 执行一个action的过程

1.  将用户请求发给org.apache.struts2.dispatcher.Dispatcher,
     wrapRequest(HttpServletRequest request, ServletContext servletContext)  方法会判断是否"multipart/form-data",如果是建立一个multiPartRequest 的实例,并且建立MultiPartRequestWrapper

写道
...if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
       MultiPartRequest multi = getContainer().getInstance(MultiPartRequest.class);
       request = new MultiPartRequestWrapper(multi, request, getSaveDir(servletContext));
} else {
       request = new StrutsRequestWrapper(request);
}
 

2. 建立 MultiPartRequestWrapper 时解析(parse) request,

public void parse(HttpServletRequest servletRequest, String saveDir)
            throws IOException {
        DiskFileItemFactory fac = new DiskFileItemFactory();
        // Make sure that the data is written to file
        fac.setSizeThreshold(0);
        if (saveDir != null) {
            fac.setRepository(new File(saveDir));
        }

        // Parse the request
        try {
            ServletFileUpload upload = new ServletFileUpload(fac);
            upload.setSizeMax(maxSize);
            //upload 解析request并取得页面参数
 	    List items = upload.parseRequest(createRequestContext(servletRequest));
 		......
	    

 3.我们看一下ServletFileUpload(commons-fileupload v1.1.1) 的parseRequest做了什么

 public List /* FileItem */ parseRequest(RequestContext ctx)
            throws FileUploadException {
        if (ctx == null) {
            throw new NullPointerException("ctx parameter");
        }

        ArrayList items = new ArrayList();
        String contentType = ctx.getContentType();

        if ((null == contentType)
            || (!contentType.toLowerCase().startsWith(MULTIPART))) {
            throw new InvalidContentTypeException(
                "the request doesn't contain a "
                + MULTIPART_FORM_DATA
                + " or "
                + MULTIPART_MIXED
                + " stream, content type header is "
                + contentType);
        }
        int requestSize = ctx.getContentLength();
        
        if (requestSize == -1) {
            throw new UnknownSizeException(
                "the request was rejected because its size is unknown");
        }
        //关键就这里了,大小超出的异常,这里是所有上传文件合计的大小,如果超出就抛出异常
        //这时上层是拿不到保存参数的items的
        if (sizeMax >= 0 && requestSize > sizeMax) {
            throw new SizeLimitExceededException(
                "the request was rejected because its size (" + requestSize
                + ") exceeds the configured maximum (" + sizeMax + ")",
                requestSize, sizeMax);
        }

        String charEncoding = headerEncoding;
        if (charEncoding == null) {
            charEncoding = ctx.getCharacterEncoding();
        }

        try {
            byte[] boundary = getBoundary(contentType);
            if (boundary == null) {
                throw new FileUploadException(
                        "the request was rejected because "
                        + "no multipart boundary was found");
            }

            InputStream input = ctx.getInputStream();

            MultipartStream multi = new MultipartStream(input, boundary);
            multi.setHeaderEncoding(charEncoding);

            boolean nextPart = multi.skipPreamble();
            while (nextPart) {
                Map headers = parseHeaders(multi.readHeaders());
                String fieldName = getFieldName(headers);
                if (fieldName != null) {
                    String subContentType = getHeader(headers, CONTENT_TYPE);
                    if (subContentType != null && subContentType
                        .toLowerCase().startsWith(MULTIPART_MIXED)) {
                        // Multiple files.
                        byte[] subBoundary = getBoundary(subContentType);
                        multi.setBoundary(subBoundary);
                        boolean nextSubPart = multi.skipPreamble();
                        while (nextSubPart) {
                            headers = parseHeaders(multi.readHeaders());
                            if (getFileName(headers) != null) {
                                FileItem item =
                                        createItem(headers, false);
                                OutputStream os = item.getOutputStream();
                                try {
                                    multi.readBodyData(os);
                                } finally {
                                    os.close();
                                }
                                items.add(item);
                            } else {
                                // Ignore anything but files inside
                                // multipart/mixed.
                                multi.discardBodyData();
                            }
                            nextSubPart = multi.readBoundary();
                        }
                        multi.setBoundary(boundary);
                    } else {
                        FileItem item = createItem(headers,
                                getFileName(headers) == null);
                        OutputStream os = item.getOutputStream();
                        try {
                            multi.readBodyData(os);
                        } finally {
                            os.close();
                        }
                        items.add(item);
                    }
                } else {
                    // Skip this part.
                    multi.discardBodyData();
                }
                nextPart = multi.readBoundary();
            }
        } catch (IOException e) {
            throw new FileUploadException(
                "Processing of " + MULTIPART_FORM_DATA
                    + " request failed. " + e.getMessage());
        }

        return items;
    }

 4.这之后才开始逐个进入interceptor,见DefaultActionInvocation.invoke()

....
//递归interceptor
if (interceptors.hasNext()) {
    			final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
    			UtilTimerStack.profile("interceptor: "+interceptor.getName(), 
    					new UtilTimerStack.ProfilingBlock<String>() {
							public String doProfiling() throws Exception {
				    			resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
				    			return null;
							}
    			});
    		} else {
                        //如果有errors,resultCode会得到‘input’
    			resultCode = invokeActionOnly();
    		}
...

 5.我们的目标就是返回input并且保留页面原来的参数,那么就要不要让ServletFileUpload抛出异常,并且要让strusts使用我们自己的jakart.

 6.写自己的ServletFileUpload

/*
 * Copyright 2001-2005 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.infowarelab.newcentury.web.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.MultipartStream;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.log4j.Logger;

/**
  * come from commons-fileupload
  * @author alfred
 */
public class ServletFileUpload extends FileUpload {

	// ---------------------------------------------------------- Class methods

	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(ServletFileUpload.class);
	private List<String> errors = new ArrayList<String>();

	/**
	 * Constructs an uninitialised instance of this class. A factory must be
	 * configured, using <code>setFileItemFactory()</code>, before attempting
	 * to parse requests.
	 * 
	 * @see FileUpload#FileUpload(FileItemFactory)
	 */
	public ServletFileUpload() {
		super();
	}

	/**
	 * Constructs an instance of this class which uses the supplied factory to
	 * create <code>FileItem</code> instances.
	 * 
	 * @see FileUpload#FileUpload()
	 */
	public ServletFileUpload(FileItemFactory fileItemFactory) {
		super(fileItemFactory);
	}
	/**
	 * overide parseRequest
	 */
	public List /* FileItem */parseRequest(RequestContext ctx) throws FileUploadException {
		if (ctx == null) {
			throw new NullPointerException("ctx parameter");
		}

		ArrayList items = new ArrayList();
		String contentType = ctx.getContentType();

		if ((null == contentType) || (!contentType.toLowerCase().startsWith(MULTIPART))) {
			throw new InvalidContentTypeException("the request doesn't contain a " + MULTIPART_FORM_DATA + " or "
					+ MULTIPART_MIXED + " stream, content type header is " + contentType);
		}
		int requestSize = ctx.getContentLength();

		if (requestSize == -1) {
			// throw new UnknownSizeException(
			// "the request was rejected because its size is unknown");
			logger.error("the request was rejected because its size is unknown");
			errors.add("the request was rejected because its size is unknown");
		}

		String charEncoding = getHeaderEncoding();
		if (charEncoding == null) {
			charEncoding = ctx.getCharacterEncoding();
		}

		try {
			byte[] boundary = getBoundary(contentType);
			if (boundary == null) {
				// throw new FileUploadException(
				// "the request was rejected because "
				// + "no multipart boundary was found");
				logger.error("the request was rejected because no multipart boundary was found");
				errors.add("the request was rejected because no multipart boundary was found");
			}

			InputStream input = ctx.getInputStream();

			MultipartStream multi = new MultipartStream(input, boundary);
			multi.setHeaderEncoding(charEncoding);

			boolean nextPart = multi.skipPreamble();
			while (nextPart) {
				Map headers = parseHeaders(multi.readHeaders());
				String fieldName = getFieldName(headers);
				if (fieldName != null) {
					String subContentType = getHeader(headers, CONTENT_TYPE);
					if (subContentType != null && subContentType.toLowerCase().startsWith(MULTIPART_MIXED)) {
						// Multiple files.
						byte[] subBoundary = getBoundary(subContentType);
						multi.setBoundary(subBoundary);
						boolean nextSubPart = multi.skipPreamble();
						while (nextSubPart) {
							headers = parseHeaders(multi.readHeaders());
							if (getFileName(headers) != null) {
								FileItem item = createItem(headers, false);
								OutputStream os = item.getOutputStream();
								try {
									multi.readBodyData(os);
								} finally {
									os.close();
								}
								items.add(item);
							} else {
								// Ignore anything but files inside
								// multipart/mixed.
								multi.discardBodyData();
							}
							nextSubPart = multi.readBoundary();
						}
						multi.setBoundary(boundary);
					} else {
						FileItem item = createItem(headers, getFileName(headers) == null);
						OutputStream os = item.getOutputStream();
						try {
							multi.readBodyData(os);
						} finally {
							os.close();
						}
						items.add(item);
					}
				} else {
					// Skip this part.
					multi.discardBodyData();
				}
				nextPart = multi.readBoundary();
			}
			// remove SizeLimitExceededException
			if (getSizeMax() >= 0 && requestSize > getSizeMax()) {
				// throw new SizeLimitExceededException(
				// "the request was rejected because its size (" + requestSize
				// + ") exceeds the configured maximum (" + getSizeMax() + ")",
				// requestSize, getSizeMax());
				logger.error("the request was rejected because its size (" + requestSize
						+ ") exceeds the configured maximum (" + getSizeMax() + ")");
			}
		} catch (IOException e) {
			logger.error("Processing of " + MULTIPART_FORM_DATA + " request failed. " + e.getMessage());
			errors.add("Processing of " + MULTIPART_FORM_DATA + " request failed. " + e.getMessage());
			// throw new FileUploadException(
			// "Processing of " + MULTIPART_FORM_DATA
			// + " request failed. " + e.getMessage());
		} 
		
		return items;
	}

	/**
	 * @return the errors
	 */
	public List<String> getErrors() {
		return errors;
	}

	/**
	 * @param errors the errors to set
	 */
	public void setErrors(List<String> errors) {
		this.errors = errors;
	}

}
 

7.copy org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest,只是import上面自己的ServletFileUpload.这样就可以保存页面的所有参数了。

8.更改struts配置文件加入你自己的JakartaMultiReques

 <bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" 
name="jakarta_yourself" 
        class="com.xxxxx.util.JakartaMultiPartRequest" 
scope="default" optional="true" />

 9.更改struts.properties

struts.multipart.parser=jakarta_yourself

10.就OK啦

 

 

分享到:
评论
2 楼 xtpgyaps 2008-12-16  
楼主,问下,,怎么我重构了JakartaMultiPartRequest这个类后,好像struts2不走我重构的这个类,怎么回事呢
1 楼 netfork 2008-12-04  
时间变化真快,楼主08年5月份发的贴,现在再看涉及到的源代码,已经变化不小了。

不过说实在的,FileUploadBase这个类写的真得不怎么样,把错误信息直接throw了,struts2的JakartaMultiPartRequest类也不怎么样,把异常的错误信息直接加到errors中就算完事了,太不负责任了吧,国际化在哪里啊?

commons-fileupload组件抛异常有他的道理,楼主的作法会引发一个大的漏洞,我作了个上传文件的完整分析,大家可以看下面的文章。
http://www.iteye.com/topic/287800

相关推荐

    struts2上传文件需要的jar包 commons-fileupload-1.2.1.jar和commons-io-1.3.2.jar

    在Struts2中处理文件上传功能时,通常需要依赖两个关键的第三方库:`commons-fileupload`和`commons-io`。这两个jar包在Java文件上传处理中扮演着至关重要的角色。 `commons-fileupload-1.2.1.jar`是Apache Commons...

    struts2 文件下载需要的架包 commons-fileupload-1.2 commons-io-1.3.2

    以上就是关于Struts2中利用Apache Commons FileUpload和Apache Commons IO进行文件上传和下载的基本原理和实现流程。这两个库大大简化了处理文件操作的复杂性,提高了代码的可读性和可维护性。在实际开发中,应根据...

    struts2文件上传下载源代码

    Struts2的Action类会接收这个文件,并使用`Commons FileUpload`库来处理文件内容。这个库处理文件分割、内存限制和临时存储等问题,确保上传过程的安全性和效率。 描述中的链接指向了CSDN博主johnjobs的一篇文章,...

    Struts2多个文件上传

    在Struts2中,文件上传功能是一个常用特性,尤其在处理用户提交的多个文件时。本文将详细讲解如何使用Struts2进行多个文件的上传,重点是使用List集合进行上传。 首先,要实现Struts2的文件上传,必须引入必要的...

    commons-fileupload-1.3.2.jar

    - Struts 2: 对于基于Struts 2的应用,可以通过配置`struts.multipart.parser`属性为`jakarta`来启用Apache Commons FileUpload。 总之,Apache Commons FileUpload是Java Web开发中处理文件上传不可或缺的工具,...

    struts2 实现文件批量上传

    1. **文件上传组件**:在Struts2中,我们通常使用`Commons FileUpload`库来处理文件上传。这个库提供了处理多部分HTTP请求的能力,是Java中处理文件上传的标准库。我们需要在Struts2配置文件中引入对应的拦截器`...

    Struts2+上传文件源码

    在Struts2中,文件上传是通过`Commons FileUpload`库来处理的,这是一个Apache提供的开源组件,专门用于处理multipart/form-data类型的表单数据,也就是通常用于文件上传的表单类型。在Struts2的Action类中,我们...

    struts2中文件上传过滤codeFilter

    Struts2提供了内置的支持来处理文件上传,主要利用了Apache Commons FileUpload库。在Struts2的Action类中,可以定义一个字段,类型为`java.io.File`或`org.apache.struts2.dispatcher.multipart.FileItem`,Struts2...

    struts2实现多文件上传功能

    这通常涉及到添加`&lt;constant&gt;`标签来设置`struts.multipart.parser`属性为`jakarta`,因为Jakarta Commons FileUpload库是Struts2处理文件上传的基础: ```xml &lt;constant name="struts.multipart.parser" value=...

    Struts2文件上传下载和表单重复提交问题

    文件上传功能在Struts2中通过`Struts2`提供的插件来实现,主要依赖于`Apache Commons FileUpload`库。首先,要在`struts.xml`配置文件中启用文件上传支持,设置`struts.multipart.parser`为`jakarta`或`native2...

    struts2上传和下载文件详细源码

    在这个"struts2上传和下载文件详细源码"中,我们可以深入理解Struts2如何处理文件上传和下载操作。 1. 文件上传: 在Struts2中,文件上传主要依赖于Apache的Commons FileUpload库。首先,需要在struts.xml配置文件...

    struts2文件上传

    5. **错误处理**:当文件上传过程中出现错误时,可以使用Struts2的异常处理机制来返回错误信息。 关于SSH(Spring、Struts2、Hibernate)标签,它们代表了三个流行Java开发框架的首字母缩写。Spring提供了依赖注入...

    struts2文件上传和下载精细讲解

    总的来说,Struts2的文件上传和下载功能通过集成Apache Commons FileUpload库,大大简化了开发者的工作,提供了友好的API和配置选项,使得处理文件上传和下载变得轻而易举。开发者只需要关注业务逻辑,无需过多关注...

    Struts2自学笔记——Struts2的文件上传

    `jakarta`是基于Apache Commons FileUpload库,而`common`则是Struts1时代的上传方式,现在已较少使用。 3. **Action类的改造** 要处理文件上传,Action类需要继承`org.apache.struts2.interceptor....

    Struts2文件上传

    - 在`struts.xml`配置文件中,需要启用`struts.multipart.parser`参数,通常设置为`jakarta`以使用Jakarta Commons FileUpload库。 - 配置Action类,声明一个或多个字段类型为`java.io.File`或`UploadedFile`,...

    struts2 上传文件源码

    - Struts2使用Apache的Commons FileUpload库来处理文件上传。这个库将文件内容分解成多个部分,以便在内存有限的情况下处理大文件。 - HTTP协议中,文件上传通过`multipart/form-data`编码类型实现。 3. **配置...

    struts2多文件上传显示进度

    Struts2通过Apache的Commons FileUpload库来处理文件上传。这个库提供了处理单个或多个文件上传的功能,并允许开发者设置最大文件大小、内存阈值等参数。在Struts2配置文件(struts.xml)中,我们需要开启文件上传...

Global site tag (gtag.js) - Google Analytics