MultiPartRequestWrapper

MultiPartRequestWrapper

本文介绍了struts2文件上传丢失参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 Struts 2.3.15.1

Using Struts 2.3.15.1

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

Implementing file upload in struts2. This is something I've done a number of times, however, I'm trying to include some sanity checks (i.e. max file size primarily). I have the fileUpload interceptor in place as the last interceptor in my stack (i.e. struts.xml). My stack includes a few in-house interceptors as well as the validationWorkflowStack. I've set the following property in my struts.properties file:

struts.multipart.maxSize = 2000000

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

In addition to the file upload, I'm passing a few other params in my form. Form is defined as:

<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>

我相信我们都知道,validationWorkflowStack 包括 params 拦截器,它将请求参数设置到操作上.这里的问题是,当上传的文件超过 ma​​xSize 时,params 拦截器没有要设置的参数.我已经通过 is 并且 actionContext 中没有任何内容.这不好,因为我需要这些参数来处理将导致的 INPUT 错误.

As I'm sure we all know, the validationWorkflowStack includes the params interceptor, which sets the request params onto the action. Here's the issue, when the file being uploaded exceeds the maxSize, there are no params for the params interceptor to set. I've stepped through is and there's nothing in the actionContext. This is not good, because I need those params to handle the INPUT error that will result.

我错过了什么吗?

推荐答案

问题解决!

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

Problem solved !

From the updated documentation, now the problem can be solved by using the new JakartaStreamMultiPartRequest :

从 Struts 版本 2.3.18 开始,MultiPartRequest 的新实现已添加 - JakartaStreamMultiPartRequest.它可以用来处理大文件,请参阅 WW-3025 了解更多详情,但您可以简单设置

<constant name="struts.multipart.parser" value="jakarta-stream" />

在 struts.xml 中开始使用它.

in struts.xml to start using it.

来自链接的 JIRA 正文:

From the linked JIRA body :

当任何大小限制超过时,立即FileUploadBase.SizeLimitExceededException 或FileUploadBase.FileSizeLimitExceededException 被抛出并解析多部分请求在不提供请求参数的情况下终止作进一步处理.

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

This basically makes it impossible for any web application to handle size limit exceeded cases gracefully.

我的建议是请求解析应该始终完成以交付请求参数.超出大小限制的情况/例外可能是收集供以后检索, FileSizeLimitExeededException 应该是映射到 FileItem 以允许对 FileItem 进行一些验证应用层面.这将允许将上传输入字段标记为如果上传的文件太大会出错.

My proposal is that request parsing should always complete to deliver the request parameters. Size limit exceeded cases/exceptions might be collected for later retrieval, FileSizeLimitExeededException should be mapped to the FileItem to allow some validation on the FileItem on application level. This would allow to mark upload input fields as erronous if the uploaded file was too big.

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

Actually I made a patch for that (see attachment). With this patch, commons-fileupload always completes request parsing in case of size limit exceedings and only after complete parsing will throw an exception if one was detected.

和克里斯克兰福德的评论:

and Chris Cranford's comment:

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

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

This multi-part parser behaves identical to the existing Jakarta multi-part parser except that it uses the Commons FileUpload Streaming API and rather than delegating maximum request size check to the File Upload API, it's done internally to avoid the existing problem of the Upload API breaking the loop iteration and parameters being lost.

太棒了,谢谢大家:)

旧答案

我猜这是由于

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

当首先解析文件时(应该取决于),如果文件打破了多部分请求大小的限制,则其他字段(表单字段)将不会被读取,因此不会与 INPUT 结果一起返回.

When the files are parsed first (it should depend on their order in the page), if a file breaks the limit of the multipart request size, the other fields (the form fields) won't be read and hence not returned back with the INPUT result.

Struts2 使用 Jakarta 实现对于 MultiPartRequestWrapper:

Struts2 uses the Jakarta implementation for the MultiPartRequestWrapper:

struts.multipart.parser - 此属性应设置为扩展 MultiPartRequest 的类.目前,该框架随 Jakarta FileUpload 实现一起提供.

Struts2 官网源码或这里(谷歌更快);这就是发布多部分表单时所称的:

You can find the source code on Struts2 official site or here (faster to google); this is what is called when posting a multipart form:

 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);
            }
        }
    }

然后,这是它循环多部分项目的地方,包括文件和表单字段:

then, this is where it cycles the multipart Items, both files and form fields:

   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);
            }
        }
    }

这将在 FileUploadBase,在这个实现中为每一项:

that will end, in the FileUploadBase, in this implementation for each item:

 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;
            }

如您所见,它处理文件大小上限和请求大小上限的方式非常不同;

as you can see, it handles pretty differently the file size cap and the request size cap;

为了好玩,我查看了源代码,但您确实可以确认(或更正)这个假设,尝试调试 MultiPartRequestWrapper 以查看内部发生的事情是否是我认为正在发生的事情......祝你好运,玩得开心.

I've looked at the source for fun but you could really confirm (or correct) this assumptions, trying to debug the MultiPartRequestWrapper to see if what happens inside is what I think is going on... good luck and have fun.

这篇关于struts2文件上传丢失参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 21:27