IT虾米网

java之struts2文件上传丢失参数

tintown 2023年10月09日 编程语言 100 0

使用 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 中什么也没有。这不好,因为我需要那些参数来处理将导致的输入错误。

我错过了什么吗?

请您参考如下方法:

问题解决了!

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

As from Struts version 2.3.18 a new implementation of MultiPartRequest was added - JakartaStreamMultiPartRequest. It can be used to handle large files, see WW-3025 for more details, but you can simple set

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

in struts.xml to start using it.

来自链接的 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.

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

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.

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.

和 Chris Cranford 的评论:

I am working on a new multipart parser for Struts2 I am calling 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.

太棒了,谢谢大家:)


旧答案

我猜这是由于不同的行为

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

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

Struts2 uses the Jakarta implementation对于 MultiPartRequestWrapper:

struts.multipart.parser - This property should be set to a class that extends MultiPartRequest. Currently, the framework ships with the Jakarta FileUpload implementation.

您可以在Struts2 官方网站上找到源代码or here (更快地谷歌);这是发布多部分表单时调用的内容:

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


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!