简体   繁体   English

Spring Boot multipartfile始终为null

[英]Spring Boot multipartfile always null

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)时,该部分始终为null。

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$1     : --> 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$1     : 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$1     : Content-Length: 181212
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig$1     : Accept: application/json
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig$1     : Authorization: Bearer [token]
2016-08-18 14:44:14.715 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig$1     : 
2016-08-18 14:44:14.735 DEBUG 13088 --- [           main] c.t.server.web.testutil.TestConfig$1     : --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$1     : --> END POST (181212-byte body)

Any ideas on what is happening? 关于发生了什么的任何想法?

You have to enable the Spring Multipart Resolver as 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. 默认情况下,Spring不进行多部分处理,因为一些开发人员希望自己处理多部分。 You enable Spring multipart handling by adding a multipart resolver to the web application's context. 您可以通过向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 MultipartFile meaning 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意味着它不是null并且在该行上进行调试显示该变量填充了我上传的图像。

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. Spring的多部分解析器要求文件的文件名出现在部件的content-disposition字段中。 Without this, it doesn't add the file into the multipart request. 没有它,它不会将文件添加到多部分请求中。

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();

You have to enable the Spring Multipart Resolver as 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. 默认情况下,Spring不进行多部分处理,因为一些开发人员希望自己处理多部分。 You enable Spring multipart handling by adding a multipart resolver to the web application's context. 您可以通过向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 StandardServletMultipartResolver instead of classic CommonsMultipartResolver 由于Spring引导1.4+使用servlet 3.0+,我们可以利用StandardServletMultipartResolver而不是经典的CommonsMultipartResolver

Please refer to my answer in this URL: Spring Boot multipart upload getting null file object 请参考此URL中的答案: Spring Boot分段上传获取空文件对象

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或boundary。

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

spring.http.multipart.enabled = true

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM