Перейти к основному содержимому

Возврат данных изображения/медиа с помощью Spring MVC

· 6 мин. чтения

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 .