2016-08-17 24 views
6

Używam Spring Boot version = '1.4.0.RC1' z Spring Boot Stormpath 1.0.2.Spring Boot plik wieloczęściowy zawsze null

Próbuję użyć pliku wieloczęściowego, ale plik MultipartFile zawsze ma wartość null w kontrolerze.

Gdy używam @RequestPart ("plik") info: "status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present"

Gdy używam @RequestPart (name = "file", wymagane = false), część jest zawsze zerowa.

Jednakże, jeśli dodaję do kontrolera argument HttpServletRequest, mogę pobrać część pliku bezpośrednio z żądania, więc wiem, że jest obecna.

Jest to kontroler, a w kodzie poniżej checkNotNull(part) zawsze udaje i checkNotNull(imageFile) zawsze kończy się niepowodzeniem:

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

Moja próba integracji wykorzystuje modernizacyjny:

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

Używam automatycznej konfiguracji więc moim jedynym zwyczajem konfiguracja klasy konfiguracji Ścieżka burzy:

@Configuration 
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter { 

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

AKTUALIZACJA: To jest żądanie wychodzące. Nie jestem pewien, jak włączyć logowanie w samym tłumaczeniu wieloczęściowym.

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) 

Jakieś pomysły na to, co się dzieje?

+0

Czy możesz podać, jak wygląda ładunek żądania? Może nawet dodać rejestrowanie poziomu debugowania do 'MultipartResolver', którego używasz do sprawdzenia, czy interpretuje on składową wieloczęściową żądania? –

+0

@ shawn-clark To jest wychodzące żądanie. Nie jestem pewien, jak włączyć logowanie w samym tłumaczeniu wieloczęściowym. Nagłówki żądań: Accept: application/json autoryzacji: Nośnik [uwierzytelniania znacznik] żądania dotyczącego: --56436527-d311-4d26-8e67-27fdf6f0edb8 Content-Disposition: postać transmisji danych; name = "plik" Content-Transfer-Encoding: binarny Content-Type: image/* Content-Length: 180999 [... binarny ...] --56436527-d311-4d26-8e67- 27fdf6f0edb8-- – JabariP

+0

Podczas korzystania z wiosennego rozruchu 1.4 możesz użyć mojego rozwiązania i działa – rajadilipkolli

Odpowiedz

1

Dowiedziałem się, że problemem był sposób, w jaki budowałem moją prośbę dzięki Retrofit.

Rozdzielacz wieloczęściowy Spring wymaga nazwy pliku, aby plik znajdował się w polu dyspozycji treści części. Bez tego nie doda pliku do żądania wieloczęściowego.

Według informacji znaleźć tutaj: https://futurestud.io/blog/retrofit-2-how-to-upload-files-to-server, mój interfejs API powinny być:

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

a następnie po dokonaniu połączenia w moim teście:

// 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

Cieszę się, że to się zaczęło. Myślałem o tym samym, ale twoje przykładowe żądanie pokazuje, że nazwa jest tam jako "plik". –

2

Musisz włączyć Spring Multipart Resolver, ponieważ domyślnie Spring nie włącza funkcji wieloczęściowej.

Domyślnie wiosny nie czyni wieloczęściowy manipulację, ponieważ niektórzy deweloperzy chcą obsługiwać multiparts siebie. Włączasz obsługę wieloczęściowego wielozakresowego przez dodanie przelicznika wieloczęściowego do kontekstu aplikacji WWW .

do klasy konfiguracji, co chcesz dodać następujący Fasola:

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

* Aktualizacja * W mojej poprzedniej odpowiedzi nie było skorygować w oparciu o komentarze. Oto zaktualizowany przykład, który udało mi się uruchomić pomyślnie.

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

Jest to bardzo prosty test bez specjalnych rzeczy. Następnie tworzone wniosek listonosz i tutaj jest wezwanie próbka 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 "[email protected]" "http://localhost:8080/test/profilePhoto" 

Odpowiedź została MultipartFile co oznacza, że ​​nie była nieważna i robi debug na tej linii wskazują, że zmienna została wypełniona obrazem, że ładował.

+0

Spring Boot Web MVC automatycznie konfiguruje resolwer wieloczęściowy, więc dodanie fasoli jest niepotrzebne. Jako czek jednak dodałem, ale wciąż mam ten sam błąd. – JabariP

+0

Ah nice ... nie sprawdził dla niego AutoConfiguration, ale od tego czasu znalazł go w "MultipartAutoConfiguration". Domyślnie korzysta z 'StandardServletMultipartResolver'. Powiedziałeś, że nadal otrzymujesz błąd z 'CommonsMultipartResolver', więc wydaje się, że jest to bardziej problem z tym, w jaki sposób przychodzi żądanie. –

0

Musisz włączyć Spring Multipart Resolver jako domyślnie Spring nie włącza funkcji wieloczęściowej.

Domyślnie wiosny nie czyni wieloczęściowy manipulację, ponieważ niektórzy deweloperzy chcą obsługiwać multiparts siebie. Włączasz obsługę wieloczęściowego wielozakresowego przez dodanie przelicznika wieloczęściowego do kontekstu aplikacji WWW .

do klasy konfiguracji, co chcesz dodać następujący Fasola:

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

Od wiosny do butów 1.4+ wykorzystuje aplet 3.0+ możemy dźwignię StandardServletMultipartResolver zamiast klasycznej CommonsMultipartResolver

+0

nie rozwiązuje problemu – AutoMEta

+0

Którą wersję rozruchu sprężynowego używasz? – rajadilipkolli

+0

https: // github.com/rajadilipkolli/spring-mvc-showcase/blob/master/src/main/java/org/springframework/samples/mvc/configuration/WebMvcConfig.java # L99-L112 przykładowy kod, który działa – rajadilipkolli