【问题标题】:struts2 file upload loosing parametersstruts2文件上传丢失参数
【发布时间】:2013-08-07 18:22:19
【问题描述】:

使用 Struts 2.3.15.1

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

struts.multipart.maxSize = 2000000

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

<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 拦截器没有参数可以设置。我已经完成了,actionContext 中没有任何内容。这不好,因为我需要这些参数来处理将导致的 INPUT 错误。

我错过了什么吗?

【问题讨论】:

  • 有什么理由将fileUpload 拦截器作为你堆栈中的最后一个拦截器?
  • 相对于不同的位置或根本没有?它现在在默认堆栈中还是什么?对于它的价值,我尝试让拦截器 first/middle/last/etc 无济于事。
  • defaultStack 中的第一个或中间某处相反。拦截器的顺序很重要。 fileUploaddefaultStack 中,但不在basicStack 中。
  • 您应该发布配置文件:web.xml、struts.xml、操作代码。如果没有,很难说出你在问什么。
  • 下面的答案有什么问题?说出你认为正在发生的事情,我很高兴被证明是错误的并学习新的东西。看源代码,我觉得是这样,下个月我回到我的电脑时我会很乐意调试它

标签: java file-upload struts2 request multipartform-data


【解决方案1】:

问题解决了!

updated documentation,现在可以使用新的JakartaStreamMultiPartRequest解决问题:

从 Struts 版本 2.3.18 开始,MultiPartRequest 的新实现 已添加 - JakartaStreamMultiPartRequest。它可以用来处理 大文件,详见WW-3025,但可以简单设置

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

在 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 结果重定向回来,并且
  • 违反整个请求的最大大小,这将(可能?)破坏任何其他元素解析,因为它是一种安全机制,而不是像文件大小检查这样的功能;

首先解析文件时 (it should depend on their order in the page),如果文件超出了多部分请求大小的限制,则其他字段(表单字段)将不会被读取,因此不会与 INPUT 结果一起返回。

Struts2 uses the Jakarta implementation 用于 MultiPartRequestWrapper:

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

可以在Struts2官网or here找到源代码(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);
            }
        }
    }

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

   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 中结束,在此实现中为每个项目:

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

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

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

【讨论】:

  • 我在这里没有看到任何建议,而是对正在发生的事情的解释,我知道。 MultiPartRequest 实现在处理其他表单变量之前添加了一个操作错误并退出。流程继续沿着拦截器堆栈向下,最终导致问题。我尝试的解决方案涉及将 fileUpload 放在首位,然后由工作流拦截器跟踪它,以在存在任何错误时进行救助,并具有一个独特的结果类型,我将其映射到对用户非常不友好的页面,因为我丢失了我的请求参数。
  • IF 这就是正在发生的事情(由于安全原因,请求被切断)THEN 你无能为力;总会有一个限制,一旦达到,就会切断请求,即使没有设置限制(这将是默认限制)。如果不是,那么解释为什么不和如何。我不是 100% 确定它是那样的,只是通过查看源代码确信它是那样的。不是每个问题都可以用“建议”来回答,但如果是这样,这也是正确的答案。唯一的建议是使用 HTML5 方式检查文件大小客户端。
  • 目前我想出的解决方案似乎运行良好。
  • 我现在对其进行了测试,就是这样 ;) 在您的回答中,您只是选择将用户重定向到错误页面,而不是将其重定向到字段空白的源页面。它可能会更好或不会,但我宁愿称其为“解决方案”。 Multipart size check 绝对是一种安全措施,不应该处理得很好;只需使用足够高的值,将 文件大小 检查服务器端和客户端,如果用户试图打破您的多部分限制,他不一定值得被重定向回所有字段人口稠密,因为可能正试图淹没。
【解决方案2】:

以下是我解决此问题的方法。我不会将此称为解决方案。

【讨论】:

    【解决方案3】:

    尝试在早期进行 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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-16
      • 2012-01-09
      • 1970-01-01
      • 2011-12-02
      • 2016-03-04
      • 2016-09-21
      • 2012-12-09
      • 2012-10-16
      相关资源
      最近更新 更多