Spring Boot multipartfile всегда null
Я использую версию Spring Boot = ' 1.4.0.RC1 ' с Spring Boot Stormpath 1.0.2.
Я пытаюсь использовать составные загрузить файл, но MultipartFile всегда имеет значение null в контроллере.
когда я использую @RequestPart ("файл") информация: "status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present"
когда я использую @RequestPart (name = "file", required = false), часть всегда равна null.
однако, если я добавлю аргумент HttpServletRequest к контроллеру, я могу получить часть файла непосредственно из просьба, поэтому я знаю, что она действительно присутствует.
это контроллер и в коде ниже 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();
}
мой интеграционный тест использует 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);
}
Я использую автоматическую конфигурацию, поэтому мой единственный пользовательский класс конфигурации настраивает Stormpath:
@Configuration
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.apply(stormpath());
}
}
обновление: Это исходящий запрос. Я не уверен, как включить ведение журнала в составном преобразователе себя.
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)
любые идеи о том, что происходит?
5 ответов
вы должны включить Spring Multipart Resolver так как по умолчанию Spring не включает функцию multipart.
по умолчанию Spring не выполняет многоступенчатую обработку, потому что некоторые разработчики хотите сами справиться с многопартийностью. Вы включаете Spring multipart обработка путем добавления составного преобразователя в веб-приложение контекст.
к вашему классу конфигурации вы хотели бы добавить следующее бин:
@Bean
public MultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
* обновление * Поскольку мой предыдущий ответ не был правильным на основе комментариев. Вот обновленный пример, который мне удалось запустить успешно.
@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);
}
}
}
Это очень простой тест без специальных материалов. Затем я создал запрос почтальона, и вот пример вызова 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"
ответ MultipartFile
означает, что это не было null и выполнение отладки в этой строке показало, что переменная была заполнена изображением, которое Я загружал.
я узнал, что проблема заключалась в том, как я строил свой запрос с помощью Retrofit.
многоэлементный преобразователь Spring требует, чтобы имя файла присутствовало в поле content-disposition детали. Без этого он не добавляет файл в составной запрос.
согласно информации, найденной здесь: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);
и тогда, когда делая вызов в моем тесте:
// 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();
вы должны включить Spring Multipart Resolver поскольку по умолчанию Spring не включает функцию multipart.
по умолчанию Spring не выполняет многоэлементную обработку, потому что некоторые разработчики хотите сами справиться с многопартийностью. Вы включаете Spring multipart обработка путем добавления составного преобразователя в веб-приложение контекст.
к вашему классу конфигурации вы хотели бы добавить следующее бин:
@Bean
public MultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
поскольку spring boot 1.4 + использует сервлет 3.0+, мы можем использоватьStandardServletMultipartResolver
вместо классического CommonsMultipartResolver
пожалуйста, обратитесь к моему ответу в этом URL: Spring Boot multipart upload получение нулевого объекта файла
в основном вам нужно избегать конфигурации Spring по умолчанию для multipart и избегать установки типа контента или границы в заголовке запроса.