1. Обзор
В этом руководстве мы покажем, как возвращать изображения и другие медиаданные с помощью среды Spring MVC.
Мы обсудим несколько подходов, начиная с прямого манипулирования HttpServletResponse
и заканчивая подходами, которые выигрывают от преобразования сообщений , согласования содержимого и абстракции ресурсов
Spring . Мы подробно рассмотрим каждый из них и обсудим их преимущества и недостатки.
2. Использование HttpServletResponse
Самый простой подход к загрузке изображения заключается в том, чтобы напрямую работать с объектом ответа и имитировать чистую реализацию
сервлета
, и это продемонстрировано с помощью следующего фрагмента:
@RequestMapping(value = "/image-manual-response", method = RequestMethod.GET)
public void getImageAsByteArray(HttpServletResponse response) throws IOException {
InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg");
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
IOUtils.copy(in, response.getOutputStream());
}
Выполнение следующего запроса отобразит изображение в браузере:
http://localhost:8080/spring-mvc-xml/image-manual-response.jpg
Реализация довольно прямолинейна и проста благодаря IOUtils
из пакета org.apache.commons.io .
Однако недостатком этого подхода является то, что он неустойчив к возможным изменениям. Тип пантомимы жестко закодирован, и изменение логики преобразования или экстернализация местоположения изображения требует изменений в коде.
В следующем разделе обсуждается более гибкий подход.
3. Использование HttpMessageConverter
В предыдущем разделе обсуждался базовый подход, в котором не используются преимущества функций преобразования сообщений и согласования содержимого в Spring MVC Framework. Для загрузки этих функций нам необходимо:
- Аннотируйте метод контроллера аннотацией
@ResponseBody.
- Зарегистрируйте соответствующий преобразователь сообщений на основе возвращаемого типа метода контроллера ( например,
ByteArrayHttpMessageConverter
необходим для правильного преобразования массива байтов в файл изображения)
3.1. Конфигурация
Для демонстрации конфигурации преобразователей мы будем использовать встроенный ByteArrayHttpMessageConverter
, который преобразует сообщение всякий раз, когда метод возвращает тип byte[]
.
ByteArrayHttpMessageConverter зарегистрирован по умолчанию, но конфигурация аналогична для
любого другого встроенного или пользовательского преобразователя.
Применение bean-компонента преобразования сообщений требует регистрации соответствующего bean-компонента MessageConverter
в контексте Spring MVC и настройки типов мультимедиа, которые он должен обрабатывать. Вы можете определить его через XML, используя тег <mvc:message-converters>
.
Этот тег должен быть определен внутри тега <mvc:annotation-driven>
, как в следующем примере:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>image/jpeg</value>
<value>image/png</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Вышеупомянутая часть конфигурации зарегистрирует ByteArrayHttpMessageConverter
для типов содержимого ответа image/jpeg
и image/png .
Если тег <mvc:message-converters>
отсутствует в конфигурации mvc, будет зарегистрирован набор конвертеров по умолчанию.
Кроме того, вы можете зарегистрировать конвертер сообщений, используя конфигурацию Java :
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(byteArrayHttpMessageConverter());
}
@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
3.2. Реализация
Теперь мы можем реализовать наш метод, который будет обрабатывать запросы на мультимедиа. Как было сказано выше, вам нужно пометить метод вашего контроллера аннотацией @ResponseBody
и использовать byte[]
в качестве возвращаемого типа:
@RequestMapping(value = "/image-byte-array", method = RequestMethod.GET)
public @ResponseBody byte[] getImageAsByteArray() throws IOException {
InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg");
return IOUtils.toByteArray(in);
}
Чтобы протестировать метод, выполните следующий запрос в браузере:
http://localhost:8080/spring-mvc-xml/image-byte-array.jpg
С другой стороны, метод ничего не знает о HttpServletResponse,
процесс преобразования легко настраивается, начиная от использования доступных преобразователей и заканчивая указанием пользовательского. Тип содержимого ответа не обязательно должен быть жестко закодирован, он будет согласовываться на основе суффикса пути запроса .jpg
.
Недостаток этого подхода в том, что вам нужно явно реализовать логику получения изображения из источника данных (локальный файл, внешнее хранилище и т. д.), и вы не имеете контроля над заголовками или кодом состояния ответа.
4. Использование класса ResponseEntity
Вы можете вернуть изображение как byte[]
, обернутое в Response Entity
. Spring MVC ResponseEntity
позволяет управлять не только телом ответа HTTP, но также заголовком и кодом состояния ответа. Следуя этому подходу, вам необходимо определить возвращаемый тип метода как ResponseEntity<byte[]>
и создать возвращаемый объект ResponseEntity
в теле метода.
@RequestMapping(value = "/image-response-entity", method = RequestMethod.GET)
public ResponseEntity<byte[]> getImageAsResponseEntity() {
HttpHeaders headers = new HttpHeaders();
InputStream in = servletContext.getResourceAsStream("/WEB-INF/images/image-example.jpg");
byte[] media = IOUtils.toByteArray(in);
headers.setCacheControl(CacheControl.noCache().getHeaderValue());
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(media, headers, HttpStatus.OK);
return responseEntity;
}
Использование ResponseEntity
позволяет настроить код ответа для данного запроса.
Явная установка кода ответа особенно полезна перед лицом исключительного события, например, если изображение не было найдено ( FileNotFoundException
) или повреждено ( IOException)
. В этих случаях все, что необходимо, — это установить код ответа, например , new ResponseEntity<>(null, headers, HttpStatus.NOT_FOUND)
в соответствующем блоке catch.
Кроме того, если вам нужно установить какие-то определенные заголовки в вашем ответе, этот подход является более простым, чем установка заголовков с помощью объекта HttpServletResponse
, который принимается методом в качестве параметра. Это делает сигнатуру метода четкой и целенаправленной.
5. Возврат изображения с помощью класса ресурсов
Наконец, вы можете вернуть изображение в виде объекта Resource
.
Интерфейс ресурсов
— это интерфейс для абстрагирования доступа к низкоуровневым ресурсам. Он представлен в Spring как более мощная замена стандартному классу java.net.URL .
Он обеспечивает легкий доступ к различным типам ресурсов (локальным файлам, удаленным файлам, ресурсам пути к классам) без необходимости написания кода, который явно извлекает их.
Чтобы использовать этот подход, тип возвращаемого значения метода должен быть установлен на Resource
, и вам необходимо аннотировать метод аннотацией @ResponseBody
.
5.1. Реализация
@ResponseBody
@RequestMapping(value = "/image-resource", method = RequestMethod.GET)
public Resource getImageAsResource() {
return new ServletContextResource(servletContext, "/WEB-INF/images/image-example.jpg");
}
или, если мы хотим больше контролировать заголовки ответа:
@RequestMapping(value = "/image-resource", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<Resource> getImageAsResource() {
HttpHeaders headers = new HttpHeaders();
Resource resource =
new ServletContextResource(servletContext, "/WEB-INF/images/image-example.jpg");
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
Используя этот подход, вы рассматриваете изображения как ресурсы, которые можно загрузить с помощью реализации интерфейса ResourceLoader .
В таком случае вы абстрагируетесь от точного местоположения вашего изображения, а ResourceLoader
решает, откуда оно загружено.
Он обеспечивает общий подход к управлению расположением изображений с помощью конфигурации и устраняет необходимость написания кода для загрузки файлов.
6. Заключение
Среди вышеупомянутых подходов мы начали с базового подхода, а затем использовали подход, который выигрывает от функции преобразования сообщений платформы. Мы также обсудили, как получить набор кода ответа и заголовков ответа без прямой передачи объекта ответа.
Наконец, мы добавили гибкости с точки зрения расположения изображений, поскольку то, откуда извлекать изображение, определяется в конфигурации, которую легче изменить на лету.
Загрузка изображения или файла с помощью Spring объясняет, как добиться того же с помощью Spring Boot.
Пример кода после руководства доступен на GitHub .