struts2文件上传松动参数

fmp*_*dmb 6 java struts2 file-upload multipartform-data request

使用Struts 2.3.15.1

在struts2中实现文件上传.这是我已经多次做过的事情,但是,我试图包括一些健全性检查(主要是最大文件大小).我将fileUpload拦截器作为我的堆栈中的最后一个拦截器(即struts.xml).我的堆栈包括一些内部拦截器以及validationWorkflowStack.我在struts.properties文件中设置了以下属性:

struts.multipart.maxSize = 2000000
Run Code Online (Sandbox Code Playgroud)

除了文件上传之外,我还在表单中传递了一些其他参数.表格定义为:

<s:form action="addResource" method="post" enctype="multipart/form-data"> 
  <s:hidden name="rfqId" value='%{rfq.id}' />
  <s:file name="uploadFile" id="uploadFile" label="File" size="40" value=""/>
  ....
</s:form>
Run Code Online (Sandbox Code Playgroud)

我相信大家都知道,validationWorkflowStack包含了params拦截器,它将请求参数设置为动作.这是问题,当上传的文件超过maxSize时,params拦截器没有设置的参数.我已经介入了,而actionContext中没有任何内容.这不好,因为我需要那些参数来处理将导致的INPUT错误.

我错过了什么吗?

And*_*ios 6

问题解决了 !

更新的文档中,现在可以使用新的JakartaStreamMultiPartRequest解决问题:

从Struts版本2.3.18开始,添加了一个新的MultiPartRequest实现 - JakartaStreamMultiPartRequest.它可用于处理大文件,有关详细信息,请参阅WW-3025,但您可以进行简单设置

<constant name="struts.multipart.parser" value="jakarta-stream" />
Run Code Online (Sandbox Code Playgroud)

在struts.xml中开始使用它.

来自链接的JIRA机构:

当任何大小限制超过时,将立即抛出FileUploadBase.SizeLimitExceededException或FileUploadBase.FileSizeLimitExceededException,并且终止对多部分请求的解析,而不提供用于进一步处理的请求参数.

这基本上使得任何Web应用程序都无法优雅地处理超出大小限制的情况.

我的建议是,请求解析应始终完成以传递请求参数.超出大小限制的情况/异常可能被收集以供以后检索,FileSizeLimitExeededException应映射到FileItem以允许在应用程序级别上对FileItem进行一些验证.如果上传的文件太大,这将允许将上传输入字段标记为错误.

实际上我做了一个补丁(参见附件).使用此补丁,commons-fileupload总是在大小限制超出的情况下完成请求解析,并且只有在完成解析后才会在检测到异常时抛出异常.

和Chris Cranford的评论:

我正在为Struts2开发一个新的多部分解析器我正在调用JakartaStreamMultiPartRequest.

这个多部分解析器的行为与现有的Jakarta多部分解析器相同,只是它使用Commons FileUpload Streaming API而不是将最大请求大小检查委托给文件上载API,它是在内部完成的,以避免上传API的现有问题打破循环迭代和参数丢失.

太棒了,谢谢你们:)


老答案

我想这是由于不同的行为

  • 单个文件(或多个文件)超过其最大定义大小,然后可以在正常过程结束时使用INPUT结果重定向回来,并且
  • 违反整个Request的最大大小,会(可能?)破坏任何其他元素解析,因为它是一种安全机制,而不是像文件大小检查那样的功能;

首先解析文件(应该取决于它们在页面中的顺序),如果文件突破了多部分请求大小的限制,其他字段(表单字段)将不会被读取,因此不会返回INPUT结果.

Struts2使用 MultiPartRequestWrapper 的Jakarta实现:

struts.multipart.parser - 此属性应设置为扩展MultiPartRequest的类.目前,该框架附带了Jakarta FileUpload实现.

您可以在Struts2官方网站或此处找到源代码(更快到google); 这是发布多部分表单时调用的内容:

 public void parse(HttpServletRequest request, String saveDir) throws IOException {
        try {
            setLocale(request);
            processUpload(request, saveDir);
        } catch (FileUploadBase.SizeLimitExceededException e) {
            if (LOG.isWarnEnabled()) {
                LOG.warn("Request exceeded size limit!", e);
            }
            String errorMessage = buildErrorMessage(e, new Object[]{e.getPermittedSize(), e.getActualSize()});
            if (!errors.contains(errorMessage)) {
                errors.add(errorMessage);
            }
        } catch (Exception e) {
            if (LOG.isWarnEnabled()) {
                LOG.warn("Unable to parse request", e);
            }
            String errorMessage = buildErrorMessage(e, new Object[]{});
            if (!errors.contains(errorMessage)) {
                errors.add(errorMessage);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后,这是循环多部分项目,文件和表单字段的位置:

   private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {
        for (FileItem item : parseRequest(request, saveDir)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Found item " + item.getFieldName());
            }
            if (item.isFormField()) {
                processNormalFormField(item, request.getCharacterEncoding());
            } else {
                processFileField(item);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

这将在FileUploadBase中针对每个项目在此实现中结束:

 FileItemStreamImpl(String pName, String pFieldName,
                    String pContentType, boolean pFormField,
                    long pContentLength) throws IOException {
                name = pName;
                fieldName = pFieldName;
                contentType = pContentType;
                formField = pFormField;
                final ItemInputStream itemStream = multi.newInputStream();
                InputStream istream = itemStream;
                if (fileSizeMax != -1) {
                    if (pContentLength != -1
                            &&  pContentLength > fileSizeMax) {
                        FileSizeLimitExceededException e =
                            new FileSizeLimitExceededException(
                                format("The field %s exceeds its maximum permitted size of %s bytes.",
                                       fieldName, fileSizeMax),
                                pContentLength, fileSizeMax);
                        e.setFileName(pName);
                        e.setFieldName(pFieldName);
                        throw new FileUploadIOException(e);
                    }
                    istream = new LimitedInputStream(istream, fileSizeMax) {
                        @Override
                        protected void raiseError(long pSizeMax, long pCount)
                                throws IOException {
                            itemStream.close(true);
                            FileSizeLimitExceededException e =
                                new FileSizeLimitExceededException(
                                    format("The field %s exceeds its maximum permitted size of %s bytes.",
                                           fieldName, pSizeMax),
                                    pCount, pSizeMax);
                            e.setFieldName(fieldName);
                            e.setFileName(name);
                            throw new FileUploadIOException(e);
                        }
                    };
                }
                stream = istream;
            }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它处理的文件大小上限和请求大小上限差别很大;

我看了很有趣的源代码,但你可以确认(或纠正)这些假设,尝试调试MultiPartRequestWrapper,看看里面发生的是我认为发生了什么......祝你好运,玩得开心.

  • 我在这里没有看到任何建议,而是对我所知道的正在发生的事情的解释.MultiPartRequest实现正在添加操作错误并在处理其他表单变量之前退出.流程继续沿拦截器堆栈向下流动,最终导致问题.我尝试过的解决方案涉及首先放置fileUpload,然后由工作流拦截器跟踪它,如果存在任何错误并且具有唯一的结果类型,我将其映射到用户的一些非常不友好的页面,因为我丢失了我的请求参数. (2认同)