6

使用 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拦截器,它将请求参数设置到操作上。这是问题所在,当上传的文件超过maxSize时,没有参数可供params拦截器设置。我已经完成了,actionContext 中没有任何内容。这不好,因为我需要这些参数来处理将导致的 INPUT 错误。

我错过了什么吗?

4

3 回答 3

6

问题解决了 !

更新的文档中,现在可以使用新的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 总是在超出大小限制的情况下完成请求解析,并且只有在完成解析后如果检测到异常才会抛出异常。

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

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

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

太棒了,谢谢大家:)


旧答案

我想这是由于不同的行为

  • 超过其最大定义大小的单个文件(或多个文件),然后可以在正常进程结束时使用 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);
            }
        }
    }

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

   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 以查看内部发生的事情是否是我认为正在发生的事情......祝你好运并玩得开心。

于 2013-08-06T23:56:46.903 回答
1

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

在此处输入图像描述

于 2013-08-13T15:52:34.210 回答
0

尝试在早期阶段进行 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>
于 2013-09-06T03:21:56.200 回答