Spring 3.0 FileUpload只用POST?

时间:2011-11-02 14:03:24

标签: spring file-upload spring-mvc

我正在尝试使用spring 3上传一个带有一个参数的文件。

这是我应该启用此服务的控制器方法:

@RequestMapping(value="/{id}", method = RequestMethod.PUT, headers="content-type=multipart/form-data")
public ResponseEntity<String> uploadImageWithJsonParamater(@PathVariable("id") Long id, @RequestParam String json, @RequestParam MultipartFile customerSignFile) {
    //...
}

问题是,服务器无法调度到此方法: MissingServletRequestParameterException:必需的String参数'json'不存在

如果我将RequestMethod从PUT更改为POST,一切都很好。那么有人知道这个问题吗?

似乎不允许通过PUT传输表单数据。

我调试了一下,以下方法在PUT情况下返回false但在POST情况下为true:

public boolean isMultipart(HttpServletRequest request) {
    return (request != null && ServletFileUpload.isMultipartContent(request));
}

我将不胜感激任何帮助!

提前致谢!

4 个答案:

答案 0 :(得分:5)

您可以使用spring HiddenHttpMethodFilter来完成此操作,但是您需要确保在Web.xml过滤器链中的HiddenHttpMethodFilter之前放置一个Spring MultipartFilter

例如:在您的web.xml中

<filter>
    <filter-name>MultipartFilter</filter-name>
    <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    <init-param>
        <param-name>multipartResolverBeanName</param-name>
        <param-value>filterMultipartResolver</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>MultipartFilter</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>
<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <servlet-name>/*</servlet-name>
</filter-mapping>

然后在spring-config.xml中添加对CommonsMultipartResolver的引用:

<bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

请注意,如果您不添加spring-config.xml条目,则MultipartFilter将默认使用使用servlet规范3.0实现的MultipartResolver,并将抛出如下错误: NoSuchMethodError HttpServletRequest.getParts()如果你没有使用3.0。

答案 1 :(得分:4)

我遇到了同样的问题。 我的解决方案是实现ExtendedMultipartResolver,它接受​​通过http方法PUT发送的多部分。 只需复制CommonsMultipartResolver代码,重命名该类并根据需要更改isMultipart()函数的实现:

private boolean isMultipartContent(HttpServletRequest request) {
  String httpMethod = request.getMethod().toLowerCase();
  // test for allowed methods here...
  String contentType = request.getContentType();
  return (contentType != null && contentType.toLowerCase().startsWith("multipart"));
}


public boolean isMultipart(HttpServletRequest request) {
      return (request != null && isMultipartContent(request));
}

您可以在此处检查POST,PUT或其他HTTP方法。就我而言,所有方法都会被接受, 因为我的控制器注释将过滤到允许的方法。

不要忘记在spring web上下文中配置bean:

<bean id="multipartResolver" class="sample.package.ExtendedMultipartResolver"/>
希望有所帮助。

欢呼声 克里斯

答案 2 :(得分:2)

您无法根据HTML标准通过PUT发送表单数据。你只能通过PUT发送文件,在这种情况下,它们发送效率更高,然后使用POST(因为你不再拥有所有的多部分开销),但是为了让你PUT监听服务器端组件实际接收文件通过PUT你必须确保你实际发送一个PUT命令(例如通过javascript)。这是一个使用JQuery的例子:

$('#file_upload').fileUpload({
    namespace: 'file_upload',
    url: '/path',
    method: 'PUT'
});

答案 3 :(得分:1)

我实施了一个略有不同的解决方案,灵感来自http://rugal.ga/development/2015/10/03/uploading-file-other-than-post/,并接近上一篇文章:

public class ExtendedMultipartResolver extends CommonsMultipartResolver {

@Override
public boolean isMultipart(HttpServletRequest request) {
    return (request != null && isMultipartContent(request));
}

/**
 * Extends ServletFileUpload.isMultipartContent() behavior to allow PUT requests as multipart.
 * 
 * @param request
 *            The servlet request to be evaluated. Must be non-null.
 *
 * @return <code>true</code> if the request is multipart; <code>false</code> otherwise.
 * @see org.apache.commons.fileupload.servlet.ServletFileUpload#isMultipartContent
 */
public static final boolean isMultipartContent(HttpServletRequest request) {
    HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod());
    if (HttpMethod.POST != httpMethod && HttpMethod.PUT != httpMethod) {
        return false;
    }
    return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}

}

这里没什么新东西,除了我的代码依赖于org.apache.commons.fileupload.FileUploadBase.isMultipartContent(RequestContext)而不是复制其内容。

现在,如果你需要实现文件上传服务的单元测试,你可能会以下列方式使用org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload():

@Test
public void testUploadFilePost() throws Exception {
    MockMultipartFile multipartFile = new MockMultipartFile(...);
    String url = ...;
    mockMvc.perform(fileUpload(url).file(multipartFile)).andExpect(status().isOk());
}

上面的代码只能发出POST请求,因为org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder的实现方式。为了对PUT文件上传进行单元测试,您可能会对以下代码感兴趣:

private static final RequestPostProcessor PUT_REQUEST_POST_PROCESSOR = new RequestPostProcessor() {

    @Override
    public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
        request.setMethod(HttpMethod.PUT.name());
        return request;
    }

};

private static MockHttpServletRequestBuilder putFileUpload(String url, MockMultipartFile multipartFile,
        Object... urlVariables) {
    return fileUpload(url, urlVariables).file(multipartFile).with(PUT_REQUEST_POST_PROCESSOR);
}

现在可以通过以下方式调整测试:

@Test
public void testUploadFilePut() throws Exception {
    MockMultipartFile multipartFile = new MockMultipartFile(...);
    String url = ...;
    mockMvc.perform(putFileUpload(url, multipartFile)).andExpect(status().isOk());
}