Как передать MultipartFile в RequestBody для контроллера spring boot rest

#spring #spring-boot

#spring #spring-boot

Вопрос:

Итак, я видел примеры, когда MultiPartFile тип передается, @RequestParam а не @RequestBody вводится. Кажется, это очень обычный способ, которым люди предлагают использовать содержимое файла @RestController примерно так

 public ResponseEntity<String> submitFile(@RequestParam(value="file") MultipartFile file)
  

Мне интересно, как это хорошая практика, поскольку данные файла передаются в URL. Почему бы не передать его @RequestBody вместо этого?

Итак, я изменил приведенный выше код на что-то вроде этого

 public ResponseEntity<String> submitFile(@RequestBody MyCustomObj myObj)
  

myCustomObj является ли pojo только одним полем с именем file типа MultipartFile

Проблема в том, что у меня есть только swagger и postman для его тестирования, и когда я использую этот @RequestBody подход, ни один из них не даст мне возможность загрузить файл, как это было бы в случае передачи MultipartFile RequestParam .

Может кто-нибудь, пожалуйста, пролить больше света на это и сказать мне, как правильно это сделать?

Комментарии:

1. Возможно, я смогу дать несколько советов, но какова ваша конечная цель здесь? Чтобы сохранить файл на сервере или каком-либо другом устройстве хранения? Чтобы сохранить файл и связать его с объектом Spring Data?

2. В конечном итоге я могу связать его с объектом spring data, и именно поэтому я выбрал подход RequestBody

Ответ №1:

В качестве альтернативы и на основе ваших комментариев я бы порекомендовал вам взглянуть на проект сообщества под названием Spring Content . Это обеспечивает абстракцию ресурсов над хранилищем, предоставляя гибкость в отношении того, где хранится ваш контент, и внедряет для вас реализации служб и контроллеров, так что вам не нужно реализовывать их самостоятельно. Кроме того, как вы упомянули, это может стать важным, Spring Content позволяет вам также связывать загруженный контент с объектами Spring Data.

Добавление его в ваш проект будет выглядеть примерно так:

pom.xml (предполагая, что maven. Также доступны устройства для запуска Spring boot)

     <!-- Java API -->
    <!-- just change this depdendency if you want to store somewhere else -->
    <dependency>
        <groupId>com.github.paulcwarren</groupId>
        <artifactId>spring-content-fs</artifactId>
        <version>0.8.0</version>
    </dependency>
    <!-- REST API -->
    <dependency>
        <groupId>com.github.paulcwarren</groupId>
        <artifactId>spring-content-rest</artifactId>
        <version>0.8.0</version>
    </dependency>
  

StoreConfig.java

 @Configuration
@EnableFilesystemStores
@Import(RestConfiguration.class)
public class StoreConfig {

    @Bean
    FileSystemResourceLoader fileSystemResourceLoader() throws IOException {
        return new FileSystemResourceLoader(new File("/path/to/uploaded/files").getAbsolutePath());
    }

}
  

FileStore.java

   @StoreRestResource(path="files")
  public interface FileStore extends Store<String> {
  }
  

И это все. Хранилище файлов по сути является универсальным загрузчиком ресурсов Spring. spring-content-fs Зависимость приведет к тому, что содержимое Spring будет внедрено в реализацию на основе файловой системы. spring-content-rest Зависимость приведет к тому, что содержимое Spring также будет вводить реализацию, если @Controller она перенаправляет HTTP-запросы на методы FileStore службы.

Таким образом, теперь у вас будет полностью функциональный (POST, PUT, GET, DELETE) файловый сервис на основе REST /files , который будет использовать ваш FileStore для извлечения (и хранения) файлов /path/to/uploaded/files на вашем сервере.

Итак:

curl --upload-file some-image.jpg /files/some-image.jpg

загрузит some-image.jpg и сохранит его /path/to/uploaded/files на вашем сервере.

И:

curl /files/some-image.jpg

он снова получит его.

HTH

Введенный контроллер также поддерживает потоковое видео, если это полезно.

Позже, если / когда вы захотите связать содержимое с объектом Spring Data, все, что вам нужно будет сделать, это расширить ваше хранилище ContentStore файлов вместо Store , введите его в объект Spring Data, с которым вы связываетесь, и добавьте аннотации Spring Content к вашему объекту, следующим образом:

 //@StoreRestResource(path="files") <-- no longer required
public interface FileStore extends ContentStore<YourEntity, String> {
}

@Entity
public class YourEntity {
   @Id
   ...

   @ContentId
   private String contentId;

   @ContentLength
   private String contentLen;

   @MimeType
   private String contentType;
}
  

И это все. Как и следовало ожидать, ваши конечные точки REST изменятся, так что теперь вы адресуете содержимое, используя то же пространство URI, что и объект Spring Data. Итак:

curl --upload-file some-image.jpg /yourEntities/{yourEntityId}

загрузит some-image.jpg , сохранит его /path/to/uploaded/files на вашем сервере и свяжет с объектом yourEntityId .

И:

curl /yourEntities/{yourEntityId}

он снова получит его.

Несколько фрагментов содержимого могут быть связаны с помощью обычных @OneToOne @OneToMany ассоциаций и и соответствующим образом отражаются в URI (надеюсь) интуитивно понятным способом.

HTH

Ответ №2:

@RequestParam сопоставляется с параметрами запроса, данными формы и частями в составных запросах, а не только с параметрами запроса, как указано в официальных документах. https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html

Файлы не должны отправляться как тело запроса, сериализованное в JSON. Вместо этого вам следует использовать тип содержимого «multipart / form-data» для загрузки ваших файлов (как указано в спецификации HTML 4 ниже), и в этом случае @RequestParam будет подходящей аннотацией для использования https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

Комментарии:

1. Итак, в этом случае, когда данные формы сопоставляются с RequestParam, они не будут встроены в URL, который вы имеете в виду?