Przeslanie MultipartFile razem z RequestBody

1

Potrzebuje RESTem przeslac plik oraz jakies dane na temat tego pliku, request payload wyglada wiec mniej wiecej tak:

data class SomeRequest(
    val data: MultipartFile,
    val metadata: List<Metadata>
)

data class Metadata(
    val name: String,
    val ids: List<Int>
)

A sam controller wyglada w ten sposob

@RestController
@RequestMapping("/api")
class SomeController {

    @PostMapping("/test")
    fun test(@ModelAttribute request: SomeRequest) {
        println(request)
    }
}

W samym postmanie probuje zrobic cos takiego
screenshot-20220309143817.png

Niestety, rezultat to

> Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'someRequest' on field 'metadata': rejected value [[<EOL>    {<EOL>        "name": "file1",<EOL>        "ids": [1,2,3]<EOL>    },<EOL>    {<EOL>        "name": "file2",<EOL>        "ids": [100,101,102]<EOL>    }<EOL>]]; codes [typeMismatch.someRequest.metadata,typeMismatch.metadata,typeMismatch.java.util.List,typeMismatch];

Jakies pomysly jak moge to rozwiazac?

0

No to niekoniecznie pojdzie Ci restem, bo content nie jest poprawnym jsonem. Musisz to ogarnac „klasycznie” albo zrobic jakies base64 z tego pliku.

0

@Charles_Ray: Co rozumiesz przez

Musisz to ogarnac „klasycznie”

0

Przeslac dane jako multipart/form-data

0

@Charles_Ray: Ok, ale poza samym plikiem, potrzebuje jego metadane, żeby wiedzieć jak go procesować. Mogę je w teorii przesłać jako @RequestParam ale będzie to dość uciążliwe, z tego względu że struktura to w gruncie rzeczy lista list. Dlatego szukam sposobu jak przekazać te metadane jako json, w tym samym requeście co sam plik

0
Gazel napisał(a):

@Charles_Ray: Ok, ale poza samym plikiem, potrzebuje jego metadane, żeby wiedzieć jak go procesować. Mogę je w teorii przesłać jako @RequestParam ale będzie to dość uciążliwe, z tego względu że struktura to w gruncie rzeczy lista list. Dlatego szukam sposobu jak przekazać te metadane jako json, w tym samym requeście co sam plik

Jakie metadane ci są potrzebne?

0

@hzmzp: Chodzi mi o zwykłego dtosa, który opisuje jak procesować dany plik, np. adres email na który trzeba go wysłać, itp. W efekcie jednak struktura tego dtosa to lista list, tak jak widać to w pierwszym poście, tak więc przesłanie tego jako @RequestParam jest problematyczne, dlatego myślałem o zwykłym JSONie

0

To tak jak napisał @Charles_Ray bierzesz taki plik przepuszczasz przez base64 i wysyłasz jako string. To jest najprostsza i niezawodna metoda. Ewentualnie możesz go uploudować inną metodą i zwrócone id czy tam scierzkę do tymczasowego pliku wysyłać jako daną zamiast MultipartFile w tym dto.

1

Tylko że te pomysły z ładniem pliku do DTO trochę się sypią jak plik ma np. 100GB ;) Może jesteś w stanie przesłać te meta-dane za pomocą headerów?

0

Udało mi się uzyskać pożądany efekt używając @RequestPart

fun method(
    @RequestPart("file") file: MultipartFile,
    @RequestPart metadata: FileMetadata
)

Problem jednak pojawia się w testach... jak to wysłać?

mockMvc.perform(multipart("/api/upload")
.file(file)
.content(mapper.writeValueAsString(metadata)))

Coś takiego kończy się wyjątkiem

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Current request is not a multipart request

0

mockMvc...

Może użyj normalnego klienta http po prostu (np. https://www.tutorialspoint.com/apache_httpclient/apache_httpclient_multipart_upload.htm )? Nie wiem czy mockmvc wspiera multiparty.

0

@Shalom:

Wygląda na to, że MockMvc wspiera multipart, mogę zrobić coś takiego

mockMvc.perform(
multipart("/api/upload")
    .file(MockMultipartFile("file", byteArrayOf(1, 2, 3))))
.andExpect(status().isCreated)

W efekcie dostaje Required request part 'metadata' is not present co ma sens, bo metadata nie zostały przekazane.

Wydawałoby się, że wystarczy je dodać

mockMvc.perform(
multipart("/api/upload")
    .file(MockMultipartFile("file", byteArrayOf(1, 2, 3)))
    .part(MockPart("metadata", mapper.writeValueAsBytes(metadata))))
.andExpect(status().isCreated)

Wtedy jednak dostaje Content type 'application/octet-stream' not supported...

1 użytkowników online, w tym zalogowanych: 0, gości: 1