在启用HiddenHttpMethodFilter之后使用Spring MVC 3.0.2上载多个文件

Tin*_*iny 7 spring file-upload spring-mvc spring-3

重要提示:对于高于3.0.4的任何Spring版本,此问题完全无用,因为此版本中讨论的问题很久以前已在该版本中修复,并且在后续版本的Spring中不再可重现.


我使用的是Spring 3.0.2版.我需要使用multiple="multiple"文件浏览器的属性上传多个文件,例如,

<input type="file" id="myFile" name="myFile" multiple="multiple"/>
Run Code Online (Sandbox Code Playgroud)

(并没有使用多个文件浏览器,就像这个答案所说的那样,它确实有效,我试过).

虽然没有版本的Internet Explorer支持这种方法,除非使用适当的jQuery插件/小部件,我现在不关心它(因为大多数其他浏览器都支持这个).

这适用于commons fileupload,但除了使用RequestMethod.POSTRequestMethod.GET方法之外,我还想使用Spring支持和建议的其他请求方法,RequestMethod.PUT并且RequestMethod.DELETE在他们自己的适当位置.为此,我已经配置了Spring,HiddenHttpMethodFilter正如这个问题所示.

但即使选择了文件浏览器中的多个文件,它也可以一次只上传一个文件.在Spring控制器类中,方法映射如下.

@RequestMapping(method={RequestMethod.POST}, value={"admin_side/Temp"})
public String onSubmit(@RequestParam("myFile") List<MultipartFile> files, @ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) throws IOException, FileUploadException {
    for (MultipartFile file : files) {
        System.out.println(file.getOriginalFilename());
    }
}
Run Code Online (Sandbox Code Playgroud)

即使请求参数@RequestParam("myFile") List<MultipartFile> files是一个List类型MultipartFile(它一次只能有一个文件).


我可以找到一个可能与此博客上的多个文件一起使用的策略.我仔细检查了一遍.

解决方案2 - 使用RAW REQUEST部分下面解决方案说,

但是,如果客户端坚持使用相同的表单输入名称,例如'files []'或'files',然后使用多个文件填充该名称,则需要进行小的黑客攻击,如下所示.如上所述,如果Spring多次检测到类型文件的相同表单输入名称,则抛出异常.CommonsFileUploadSupport - 其抛出该异常的类不是最终和方法,其抛出异常保护,从而使用继承的奇迹和子类可以简单地固定/修改逻辑一点点如下.我所做的改变实际上是一个代表一个方法调用的单词,它使我们能够在同一个表单输入名下传入多个文件.

它试图覆盖该方法

protected MultipartParsingResult parseFileItems(List fileItems, String encoding){}
Run Code Online (Sandbox Code Playgroud)

CommonsFileUploadSupport扩展类的抽象类,CommonsMultipartResolver

package multipartResolver;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.fileupload.FileItem;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

public final class MultiCommonsMultipartResolver extends CommonsMultipartResolver {

    public MultiCommonsMultipartResolver() {}

    public MultiCommonsMultipartResolver(ServletContext servletContext) {
        super(servletContext);
    }

    @Override
    @SuppressWarnings("unchecked")
    protected MultipartParsingResult parseFileItems(List fileItems, String encoding) {
        Map<String, MultipartFile> multipartFiles = new HashMap<String, MultipartFile>();
        Map multipartParameters = new HashMap();

        // Extract multipart files and multipart parameters.
        for (Iterator it = fileItems.iterator(); it.hasNext();) {
            FileItem fileItem = (FileItem) it.next();

            if (fileItem.isFormField()) {
                String value = null;

                if (encoding != null) {
                    try {
                        value = fileItem.getString(encoding);
                    } catch (UnsupportedEncodingException ex) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Could not decode multipart item '" + fileItem.getFieldName()
                                    + "' with encoding '" + encoding + "': using platform default");
                        }

                        value = fileItem.getString();
                    }
                } else {
                    value = fileItem.getString();
                }

                String[] curParam = (String[]) multipartParameters.get(fileItem.getFieldName());

                if (curParam == null) {
                    // simple form field
                    multipartParameters.put(fileItem.getFieldName(), new String[]{value});
                } else {
                    // array of simple form fields
                    String[] newParam = StringUtils.addStringToArray(curParam, value);
                    multipartParameters.put(fileItem.getFieldName(), newParam);
                }
            } else {
                // multipart file field
                CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
                if (multipartFiles.put(fileItem.getName(), file) != null) {
                    throw new MultipartException("Multiple files for field name [" + file.getName()
                            + "] found - not supported by MultipartResolver");
                }

                if (logger.isDebugEnabled()) {
                    logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize()
                            + " bytes with original filename [" + file.getOriginalFilename() + "], stored "
                            + file.getStorageDescription());
                }
            }
        }

        return new MultipartParsingResult(multipartFiles, multipartParameters);
    }
}
Run Code Online (Sandbox Code Playgroud)

会发生什么是方法中的最后一行parseFileItems()(return语句)即

return new MultipartParsingResult(multipartFiles, multipartParameters);
Run Code Online (Sandbox Code Playgroud)

导致编译时错误,因为第一个参数multipartFiles是一种Map实现的类型,但实际上,它需要一个类型的参数HashMap MultiValueMap<String, MultipartFile>

它是抽象类中静态类的构造函数CommonsFileUploadSupport,

public abstract class CommonsFileUploadSupport {
    protected static class MultipartParsingResult {
        public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {}
    }
}
Run Code Online (Sandbox Code Playgroud)

原因可能是 - 这个解决方案是关于Spring版本2.5,我使用的是Spring版本3.0.2,这可能不适合这个版本.


然而,我尝试以各种方式替换Mapwith MultiValueMap,例如以下代码段中显示的那个,

MultiValueMap<String, MultipartFile>mul=new LinkedMultiValueMap<String, MultipartFile>();   

for(Entry<String, MultipartFile>entry:multipartFiles.entrySet()) {
    mul.add(entry.getKey(), entry.getValue());
}

return new MultipartParsingResult(mul, multipartParameters);
Run Code Online (Sandbox Code Playgroud)

但没有成功.我不知道如何更换Map,MultiValueMap甚至这样做也可以.执行此操作后,浏览器显示Http响应,

HTTP状态400 -

类型状态报告

信息

description客户端发送的请求在语法上是不正确的().

Apache Tomcat/6.0.26


我试图尽可能地缩短问题,但我没有包含不必要的代码.

如何配置Spring后可以上传多个文件HiddenHttpMethodFilter

该博客表明它是一个长期存在的高优先级错误.

如果没有关于版本3.0.2(3或更高版本)的解决方案,那么我必须永远禁用Spring支持并继续使用commons-fileupolad,如该博客上的第三个解决方案所建议的那样,永远省略PUT,DELETE和其他请求方法.


parseFileItems()对类中的方法中的代码进行的很少更改MultiCommonsMultipartResolver 可能会使其上传多个文件,但我的尝试无法成功(再次使用Spring 3.0.2版(3或更高版本)).

Tin*_*iny 1

问题中提到的问题已在 Spring 3.0.4 中得到修复。因此,如果您碰巧使用该版本或更高版本(是的,现在是 4.xx),您将不再需要阅读此问题/答案。