简体   繁体   English

struts2文件上传丢失参数

[英]struts2 file upload loosing parameters

Using Struts 2.3.15.1 使用Struts 2.3.15.1

Implementing file upload in struts2. 在struts2中实现文件上传。 This is something I've done a number of times, however, I'm trying to include some sanity checks (ie max file size primarily). 这是我已经做过很多次的事情,但是,我试图包括一些完整性检查(即主要是最大文件大小)。 I have the fileUpload interceptor in place as the last interceptor in my stack (ie struts.xml ). 我有fileUpload拦截器作为堆栈中的最后一个拦截器(即struts.xml )。 My stack includes a few in-house interceptors as well as the validationWorkflowStack . 我的堆栈包括一些内部拦截器以及validateWorkflowStack I've set the following property in my struts.properties file: 我在struts.properties文件中设置了以下属性:

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>

As I'm sure we all know, the validationWorkflowStack includes the params interceptor, which sets the request params onto the action. 众所周知, validationWorkflowStack包含params拦截器,该拦截器将请求params设置到操作上。 Here's the issue, when the file being uploaded exceeds the maxSize , there are no params for the params interceptor to set. 这就是问题,当要上传的文件超过maxSize时 ,就没有设置params拦截器的参数 I've stepped through is and there's nothing in the actionContext. 我已经完成了,在actionContext中什么也没有。 This is not good, because I need those params to handle the INPUT error that will result. 这不好,因为我需要那些参数来处理将导致的INPUT错误。

Am I missing something? 我想念什么吗?

Problem solved ! 问题解决了 !

From the updated documentation , now the problem can be solved by using the new JakartaStreamMultiPartRequest : 更新的文档中 ,现在可以使用新的JakartaStreamMultiPartRequest解决问题:

As from Struts version 2.3.18 a new implementation of MultiPartRequest was added - JakartaStreamMultiPartRequest. 从Struts 2.3.18版开始,添加了MultiPartRequest的新实现-JakartaStreamMultiPartRequest。 It can be used to handle large files, see WW-3025 for more details, but you can simple set 它可用于处理大文件,有关更多详细信息,请参阅WW-3025 ,但您可以简单设置

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

in struts.xml to start using it. 在struts.xml中开始使用它。

From the linked JIRA body : 从链接的JIRA主体中:

When any size limits exceed, immediately a FileUploadBase.SizeLimitExceededException or FileUploadBase.FileSizeLimitExceededException is thrown and parsing of the multipart request terminates without providing request parameters for further processing. 当任何大小限制超过时,将立即引发FileUploadBase.SizeLimitExceededException或FileUploadBase.FileSizeLimitExceededException,并且在不提供请求参数进行进一步处理的情况下,多部分请求的解析将终止。

This basically makes it impossible for any web application to handle size limit exceeded cases gracefully. 这基本上使任何Web应用程序都无法优雅地处理超出大小限制的情况。

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. 超出大小限制的情况/例外可能会被收集以供以后检索,FileSizeLimitExeededException应该映射到FileItem,以允许在应用程序级别对FileItem进行某些验证。 This would allow to mark upload input fields as erronous if the uploaded file was too big. 如果上传的文件太大,这将允许将上传输入字段标记为错误。

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. 使用此修补程序,commons-fileupload总是在超出大小限制的情况下完成请求解析,并且只有在完整解析之后,如果检测到异常,才会引发异常。

and Chris Cranford's comment: 和克里斯·克兰福德的评论:

I am working on a new multipart parser for Struts2 I am calling JakartaStreamMultiPartRequest. 我正在为Struts2开发一个新的多部分解析器,我正在调用JakartaStreamMultiPartRequest。

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. 此多部分解析器的行为与现有Jakarta多部分解析器相同,不同之处在于它使用Commons FileUpload Streaming API,而不是将最大请求大小检查委托给File Upload API,它是在内部完成的操作,以避免存在Upload API的问题破坏循环迭代和参数丢失。

Awesome, thanks guys :) 太好了,谢谢大家:)


Old answer 旧答案

I guess it is due to the different behavior of 我猜这是由于行为不同

  • a single file (or more files) that is exceeding its maximum defined size, and then can be redirected back at the end of a normal process with the INPUT result, and 超出其最大定义大小的单个文件(或多个文件),然后可以在正常过程结束时使用INPUT结果重定向回该文件,以及
  • the violation of the maximum size of the entire Request, that will (probably?) break any other element parsing, because it is a security mechanism, not a feature like the file size check; 违反整个请求的最大大小,这将(可能吗?)中断其他任何元素的解析,因为它是一种安全机制,而不是文件大小检查之类的功能;

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 uses the Jakarta implementation for the MultiPartRequestWrapper: Struts2对MultiPartRequestWrapper 使用Jakarta实现

struts.multipart.parser - This property should be set to a class that extends MultiPartRequest. struts.multipart.parser此属性应设置为扩展MultiPartRequest的类。 Currently, the framework ships with the Jakarta FileUpload implementation. 当前,该框架随Jakarta FileUpload实现一起提供。

You can find the source code on Struts2 official site or here (faster to google); 您可以在Struts2官方网站上或此处找到源代码(更快到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);
            }
        }
    }

that will end, in the FileUploadBase , in this implementation for each item: 在此实现中,对于每个项目,该操作将在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;
            }

as you can see, it handles pretty differently the file size cap and the request size cap; 如您所见,它处理文件大小上限和请求大小上限的方式大不相同;

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. 我查看了有趣的源代码,但是您可以真正确认(或纠正)这个假设,尝试调试MultiPartRequestWrapper来查看内部发生的事情是否是我认为正在发生的事情……祝您好运并玩得开心。

Here's how I've worked around this issue. 这是我解决此问题的方法。 I wouldn't call this a solution. 我不会称其为解决方案。

在此处输入图片说明

Try putting a javascript check at at the early stage : 尝试在早期阶段进行javascript检查:

<!DOCTYPE html>
<html>

    <head>
    <script type="text/javascript">
    function checkSize(max_img_size)
    {
        var input = document.getElementById("upload");
        // check for browser support (may need to be modified)
        if(input.files && input.files.length == 1)
        {           
            if (input.files[0].size > max_img_size) 
            {
                alert("The file must be less than " + (max_img_size/1024/1024) + "MB");
                return false;
            }
        }

        return true;
    }
    </script>
    </head>
    <body>
    <form action="demo_post_enctype.asp" method="post" enctype="multipart/form-data" 
    onsubmit="return checkSize(2097152)">    
    <input type="file" id="upload" />
    <input type="submit" />

    </body>
    </html>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM