问题
I am using Spring Boot version = '1.4.0.RC1' with Spring Boot Stormpath 1.0.2.
I am trying to use multipart file upload but the MultipartFile is always null in the controller.
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"
When I use @RequestPart(name = "file", required = false), the part is always 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.
This is the controller and in the code below checkNotNull(part)
always succeeds and checkNotNull(imageFile)
always fails:
@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:
@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?
回答1:
You have to enable the Spring Multipart Resolver as by default Spring doesn't enable the multipart capability.
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.
To your configuration class you would want to add the following 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 -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.
回答2:
I found out that the problem was the way I was building my request with 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.
According to the info found here: https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server, my API interface should be:
@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();
回答3:
You have to enable the Spring Multipart Resolver as by default Spring doesn't enable the multipart capability.
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.
To your configuration class you would want to add the following 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
回答4:
Please refer to my answer in this 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.
回答5:
Check if the following line is present in your application.properties:
spring.http.multipart.enabled = true
来源:https://stackoverflow.com/questions/39007726/spring-boot-multipartfile-always-null