2016-08-17 6 views
6

Я использую Spring Boot version = '1.4.0.RC1' с Spring Boot Stormpath 1.0.2.Spring Boot multipartfile всегда null

Я пытаюсь использовать многостраничную загрузку файлов, но MultipartFile всегда имеет значение null в контроллере.

Когда я использую @RequestPart ("Файл") инфо: "status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present"

Когда я использую @RequestPart (имя = "файл", требуемое = ложь), то часть всегда нуль.

Однако, если я добавлю аргумент 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(); 
} 

Мой интеграционный тест использует дооснащения:

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

UPDATE: Это исходящий запрос. Я не уверен, как включить ведение журнала в самом многопроцессорном резольвере.

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) 

Любые идеи о том, что происходит?

+0

Можете ли вы включить, как выглядит полезная нагрузка запроса? Может быть, даже добавить журнал уровня отладки в «MultipartResolver», который вы используете, чтобы проверить, интерпретирует ли он многочастный компонент запроса? –

+0

@ shawn-clark Это исходящий запрос. Я не уверен, как включить ведение журнала в самом многопроцессорном резольвере. заголовки запросов: Accept: применение/JSON Авторизация: Носитель [маркер аутентификации] Тело запроса: --56436527-d311-4d26-8e67-27fdf6f0edb8 Content-Disposition: формы-данные; Name = "файл" Content-Transfer-Encoding: двоичный Content-Type: изображение/* Content-Length: 180999 [... двоичный ...] --56436527-d311-4d26-8e67- 27fdf6f0edb8-- – JabariP

+0

Поскольку вы используете весеннюю загрузку 1.4, вы можете использовать мое решение, и оно работает – rajadilipkolli

ответ

1

Я выяснил, что проблема заключалась в том, как я строил свой запрос с помощью Модификации.

Многопараметрический преобразователь 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(); 
+0

Рад, что вы это сделали. Я думал об этом же, но ваш запрос образца показывает, что имя есть как «файл». –

2

Вы должны включить Spring Multipart Resolver, поскольку по умолчанию Spring не включает возможность многократного использования.

По умолчанию Spring не использует многопроцессорную обработку, потому что некоторые разработчики хотят обрабатывать мультипартнеры самостоятельно. Вы включаете операцию Spring multipart , добавляя многопоточный резольвер в контекст веб-приложения .

Для вашего класса конфигурации вы хотели бы добавить следующий компонент:

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

* Update * Поскольку мой предыдущий ответ не является правильным на основе комментариев. Вот обновленный пример того, что я смог успешно работать.

@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 -X POST -H "Cache-Control: no-cache" -H "Postman-Token: 17e5e6ac-3762-7d45-bc99-8cfcb6dc8cb5" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "[email protected]" "http://localhost:8080/test/profilePhoto" 

Ответ был MultipartFile это означает, что она не была пустой и делать отладку на этой линии показали, что переменная была заселена с изображением, которое я была загрузка.

+0

Spring Boot Web MVC автоматически настраивает многопоточный распознаватель, поэтому добавление этого компонента необязательно. Однако в качестве проверки я добавил ее, но все же получил ту же ошибку. – JabariP

+0

Ah nice ... не проверял AutoConfiguration для него, но с тех пор нашел его в 'MultipartAutoConfiguration'. По умолчанию используется «StandardServletMultipartResolver». Вы сказали, что по-прежнему получаете ошибку с 'CommonsMultipartResolver', поэтому кажется, что это больше проблема с тем, как поступает запрос. –

0

Вы должны включить Spring Multipart Resolver как по умолчанию Spring не разрешает многопользовательскую возможность.

По умолчанию Spring не использует многопроцессорную обработку, потому что некоторые разработчики хотят обрабатывать мультипартнеры самостоятельно. Вы включаете операцию Spring multipart , добавляя многопоточный резольвер в контекст веб-приложения .

Для вашего класса конфигурации вы хотели бы добавить следующий компонент:

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

С весной ботинки 1.4+ использует сервлет 3.0+ мы можем использовать на StandardServletMultipartResolver вместо классического CommonsMultipartResolver

+0

не решила проблему – AutoMEta

+0

Какой тип загрузки вы используете? – rajadilipkolli

+0

https: // github.com/rajadilipkolli/spring-mvc-showcase/blob/master/src/main/java/org/springframework/samples/mvc/configuration/WebMvcConfig.java # Образец кода L99-L112, который работает – rajadilipkolli

0

Пожалуйста, обратитесь к моему ответу в этом URL-адресе: Spring Boot multipart upload getting null file object

В принципе, вам необходимо избегать конфигурации Spring по умолчанию ion для multipart и избегать установки типа содержимого или границы в заголовке запроса.