#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, который вы имеете в виду?