java Spring Boot 多部分文件始终为空

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39007726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 04:00:36  来源:igfitidea点击:

Spring Boot multipartfile always null

javaspring-mvcspring-bootretrofitmultipart

提问by JabariP

I am using Spring Boot version = '1.4.0.RC1' with Spring Boot Stormpath 1.0.2.

我在 Spring Boot Stormpath 1.0.2 中使用 Spring Boot version = '1.4.0.RC1'。

I am trying to use multipart file upload but the MultipartFile is always null in the controller.

我正在尝试使用分段文件上传,但控制器中的 MultipartFile 始终为空。

When I use @RequestPart("file") the info: "status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present"

当我使用 @RequestPart("file") 信息时: "status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present"

When I use @RequestPart(name = "file", required = false), the part is always null.

当我使用@RequestPart(name = "file", required = false) 时,该部分始终为空。

However, if I add an HttpServletRequest argument to the controller, I can get the file part directly from the request, so I know that it is actually present.

但是,如果我向控制器添加一个 HttpServletRequest 参数,我可以直接从请求中获取文件部分,所以我知道它确实存在。

This is the controller and in the code below checkNotNull(part)always succeeds and checkNotNull(imageFile)always fails:

这是控制器,在下面的代码中checkNotNull(part)总是成功,checkNotNull(imageFile)总是失败:

@PostMapping("{username}/profilePhoto")
public ResponseEntity<?> saveProfilePhoto(@PathVariable("username") String username,
                                          @RequestPart(name = "file", required = false) MultipartFile imageFile,
                                          HttpServletRequest request) {
    try {
        Part part = request.getPart("file");
        checkNotNull(part);
        checkNotNull(imageFile);
    } catch (IOException | ServletException ex) {
        throw InternalServerErrorException.create();
    }

    // Transfer the multipart file to a temp file
    File tmpFile;
    try {
        tmpFile = File.createTempFile(TMP_FILE_PREFIX, null);
        imageFile.transferTo(tmpFile);
    } catch (IOException ex) {
        log.error("Failed to create temp file", ex);
        throw InternalServerErrorException.create();
    }

    // Execute the use case
    updateUserProfilePhoto.execute(username, tmpFile);

    // Delete the temp file
    FileUtils.deleteQuietly(tmpFile);

    return ResponseEntity.status(HttpStatus.CREATED).build();
}

My integration test uses retrofit:

我的集成测试使用改造:

@Multipart
@POST("users/{username}/profilePhoto")
Call<Void> uploadProfilePhoto(@Path("username") String username,
                              @Part("file") RequestBody profilePhoto);

...

@Test
public void saveProfilePhoto_shouldSavePhoto() throws IOException {
    // Given
    String usernamme = usernames[0];
    Resource testImageResource = context.getResource("classpath:images/test_image.jpg");
    File imageFile = testImageResource.getFile();
    RequestBody body = RequestBody.create(okhttp3.MediaType.parse("image/*"), imageFile);

    // When
    Response<Void> response = getTestApi().uploadProfilePhoto(usernamme, body).execute();

    // Then
    assertThat(response.code()).isEqualTo(201);
}

I am using auto configuration so my only custom config class configures Stormpath:

我正在使用自动配置,所以我唯一的自定义配置类配置 Stormpath:

@Configuration
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.apply(stormpath());
    }
}

UPDATE: This is the outgoing request. I am not sure how to enable logging in the multipart resolver itself.

更新:这是传出请求。我不确定如何启用多部分解析器本身的日志记录。

2016-08-18 14:44:14.714 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : --> POST http://localhost:8080/users/user1/profilePhoto http/1.1
2016-08-18 14:44:14.714 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : Content-Type: multipart/form-data; boundary=fe23ef21-3413-404c-a260-791c6921b2c6
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : Content-Length: 181212
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : Accept: application/json
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : Authorization: Bearer [token]
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : 
2016-08-18 14:44:14.735 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : --fe23ef21-3413-404c-a260-791c6921b2c6
Content-Disposition: form-data; name="file"
Content-Transfer-Encoding: binary
Content-Type: image/*
Content-Length: 180999

file data

--fe23ef21-3413-404c-a260-791c6921b2c6--

2016-08-18 14:44:14.762 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig     : --> END POST (181212-byte body)

Any ideas on what is happening?

关于发生了什么的任何想法?

采纳答案by JabariP

I found out that the problem was the way I was building my request with Retrofit.

我发现问题在于我使用 Retrofit 构建请求的方式。

Spring's multipart resolver requires the filename for the file to be present in content-disposition field of the part. Without this, it doesn't add the file into the multipart request.

Spring 的 multipart 解析器要求文件的文件名出现在部件的 content-disposition 字段中。没有这个,它不会将文件添加到多部分请求中。

According to the info found here: https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server, my API interface should be:

根据此处找到的信息:https: //futurestud.io/blog/retrofit-2-how-to-upload-files-to-server,我的 API 接口应该是:

@Multipart
@POST("users/{username}/profilePhoto")
Call<Void> uploadProfilePhoto(@Path("username") String username,
                              @Part MultipartBody.Part profilePhoto);

And then when making the call in my test:

然后在我的测试中拨打电话时:

// Given
String usernamme = usernames[0];
Resource testImageResource = context.getResource("classpath:images/test_image.jpg");
File imageFile = testImageResource.getFile();
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), imageFile);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", imageFile.getName(), requestFile);

// When
Response<Void> response = testApi.uploadProfilePhoto(usernamme, filePart).execute();

回答by Shawn Clark

You have to enable the Spring Multipart Resolveras by default Spring doesn't enable the multipart capability.

您必须启用Spring Multipart Resolver,因为默认情况下 Spring 不启用 multipart 功能。

By default, Spring does no multipart handling, because some developers want to handle multiparts themselves. You enable Spring multipart handling by adding a multipart resolver to the web application's context.

默认情况下,Spring 不做 multipart 处理,因为一些开发人员想要自己处理 multipart。您可以通过向 Web 应用程序的上下文添加多部分解析器来启用 Spring 多部分处理。

To your configuration class you would want to add the following bean:

在您的配置类中,您需要添加以下 bean:

@Bean
public MultipartResolver multipartResolver() {
    return new CommonsMultipartResolver();
}

* Update *As my previous answer was not correct based on the comments. Here is an updated example that I was able to run successfully.

* 更新 *因为根据评论,我之前的答案不正确。这是我能够成功运行的更新示例。

@SpringBootApplication
public class StackoverflowWebmvcSandboxApplication {
    public static void main(String[] args) {
        SpringApplication.run(StackoverflowWebmvcSandboxApplication.class, args);
    }

    @Controller
    public class UploadPhoto {
        @PostMapping("{username}/profilePhoto")
        public ResponseEntity<String> saveProfilePhoto(@PathVariable("username") String username,
                @RequestPart(name = "file", required = false) MultipartFile imageFile, HttpServletRequest request) {
            String body = "MultipartFile";
            if (imageFile == null) {
                body = "Null MultipartFile";
            }

            return ResponseEntity.status(HttpStatus.CREATED).body(body);
        }
    }
}

It is a very basic test with no special stuff. I then created a postman request and here is the sample curl call:

这是一个非常基本的测试,没有什么特别的东西。然后我创建了一个邮递员请求,这是示例 curl 调用:

curl -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 17e5e6ac-3762-7d45-bc99-8cfcb6dc8cb5" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "file=@" "http://localhost:8080/test/profilePhoto"

The response was MultipartFilemeaning that it wasn't null and doing a debug on that line showed that the variable was populated with the image that I was uploading.

响应MultipartFile意味着它不为空,并且在该行上进行调试显示该变量填充了我上传的图像。

回答by rajadilipkolli

You have to enable the Spring Multipart Resolveras by default Spring doesn't enable the multipart capability.

您必须启用Spring Multipart Resolver,因为默认情况下 Spring 不启用 multipart 功能。

By default, Spring does no multipart handling, because some developers want to handle multiparts themselves. You enable Spring multipart handling by adding a multipart resolver to the web application's context.

默认情况下,Spring 不做 multipart 处理,因为一些开发人员想要自己处理 multipart。您可以通过向 Web 应用程序的上下文添加多部分解析器来启用 Spring 多部分处理。

To your configuration class you would want to add the following bean:

在您的配置类中,您需要添加以下 bean:

@Bean
public MultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
}

Since spring boot 1.4+ uses servlet 3.0+ we can leverage on StandardServletMultipartResolverinstead of classic CommonsMultipartResolver

由于 spring boot 1.4+ 使用 servlet 3.0+ 我们可以利用StandardServletMultipartResolver而不是经典CommonsMultipartResolver

回答by Alexis Gamarra

Please refer to my answer in this URL: Spring Boot multipart upload getting null file object

请参考我在这个 URL 中的回答: Spring Boot multipart upload getting null file object

Basically you have to avoid the default Spring configuration for multipart and avoid to set Content-type or boundary in your request header.

基本上,您必须避免 multipart 的默认 Spring 配置,并避免在请求标头中设置 Content-type 或边界。

回答by Nazrul Kabir

Check if the following line is present in your application.properties:

检查您的 application.properties 中是否存在以下行:

spring.http.multipart.enabled = true