mvc - Spring Controller @RequestBody con carga de archivos ¿es posible?
upload file spring mvc (5)
Agregue el siguiente bean en su spring-servlet.xml para agregar el soporte para la solicitud de varias partes.
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
Tampoco olvides agregar la dependencia para el archivo commons-fileupload jar.
Tengo un controlador como este y quiero enviar un formulario con la carga de archivos, así como algunos datos de formularios como la etiqueta, como se muestra a continuación. Además, quiero hacer eso usando @RequestBody para poder usar la anotación @Valid en el contenedor, ya que se agregarán más variables.
public @ResponseBody WebResponse<Boolean> updateEUSettings(
final Locale locale,
@Validated @ModelAttribute final EUPSettingsWrapper endUserPortalSettingsWrapper) {
}
Y mi envoltorio es:
public class EUPSettingsWrapper {
private String label;
private MultipartFile logo;
// getter , setters..etc...
}
Pero me gustaría convertirlo en un @RequestBody de ModelAttributes.
La forma en que lo estoy intentando es tener la carga del archivo separada como un parámetro de solicitud como este:
public @ResponseBody WebResponse<Boolean> updateEUSettings(
final Locale locale,
@Validated @RequestBody final EUPSettingsWrapper endUserPortalSettingsWrapper,
@RequestParam(value = "file1", required = true) final MultipartFile logo) {
endUserPortalSettingsWrapper.setLogo(logo);
// ...
}
En mi simulacro MVC, estoy configurando:
getMockMvc().perform(fileUpload(uri).file(logo)
.accept(MediaType.APPLICATION_JSON)
.content(JSONUtils.toJSON(wrapper))
.contentType(MediaType.MULTIPART_FORM_DATA))
.andExpect(status().isOk());
Pero estoy recibiendo un error como este que dice:
org.springframework.web.HttpMediaTypeNotSupportedException: Content type ''multipart/form-data'' not supported
¿Alguien tiene una buena idea de cómo se pueden usar las cargas de archivos de varias partes con @RequestBody? ¿Algo que estoy haciendo mal arriba?
Luché un poco con esto y terminé pasando por parámetros simples. Bien si no tienes mucho que pasar en tu solicitud:
myMethod(@RequestParam("file") MultipartFile myFile,
@RequestParam("param1") Float param1, @RequestParam("param2") String param2 {}
No pude encontrar una manera de usar @RequestBody.
Sin embargo, puedes hacer algo como esto:
@RequestMapping(value = "/uploadStuff", method = RequestMethod.POST)
public MyViewDto doStuff(@RequestPart("json") @Valid MyDto dto,
@RequestPart("file") MultipartFile file) { ... }
Puedes probarlo así:
MockMultipartFile jsonFile = new MockMultipartFile("json", "",
"application/json", "{}".getBytes());
MockMultipartFile dataFile = new MockMultipartFile("file", "foo.zip", "application/octet-stream", bytes);
mockMvc.perform(fileUpload("/uploadStuff")
.file(dataFile)
.file(jsonFile))
.andExpect(status().isOk());
Para Spring 4 y versiones posteriores, puede hacer lo siguiente para obtener el objeto completo:
public ResponseEntity<Object> upload(@Payload EUPSettingsWrapper wrapper) {
}
Nota: También debería funcionar sin la etiqueta.
public ResponseEntity<Object> upload(EUPSettingsWrapper wrapper) {
}
Puede simplificar su vida aquí, ya que todo lo que está haciendo es enviar un formulario que contiene algunos campos y archivos. No necesitas @RequestBody para lo que estás tratando de hacer. Puede usar las funciones regulares de Spring MVC, por lo que su método de control sería:
@ResponseBody
public WebResponse<Boolean> updateEUSettings(
Locale locale,
@Valid EUPSettingsWrapper endUserPortalSettingsWrapper,
@RequestParam(value = "file1", required = true) MultipartFile logo
) {
}
El cliente que envíe la solicitud a este controlador deberá tener un formulario con enctype="multipart/form-data"
.
En tu prueba Spring MVC escribirías algo como esto:
getMockMvc().perform(fileUpload(uri).file("file1", "some-content".getBytes())
.param("someEuSettingsProperty", "someValue")
.param("someOtherEuSettingsProperty", "someOtherValue")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.MULTIPART_FORM_DATA))
.andExpect(status().isOk());