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

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

1. обзор

В этом руководстве мы проиллюстрируем, как возвращать изображения и другие мультимедийные данные с помощью инфраструктуры Spring MVC.

Мы обсудим несколько подходов, начиная с прямого управленияHttpServletResponse, чем переходя к подходам, которые выигрывают от абстракцииMessage Conversion,Content Negotiation и SpringResource. Мы подробно рассмотрим каждый из них и обсудим их преимущества и недостатки.

2. ИспользуяHttpServletResponse

Самый простой подход к загрузке изображения - это прямая работа с объектомresponse и имитация чистой реализацииServlet, и это продемонстрировано с помощью следующего фрагмента:

@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. Однако недостатком этого подхода является то, что он не устойчив к потенциальным изменениям. Тип MIME жестко запрограммирован, и изменение логики преобразования или экстернализация местоположения изображения требует изменений в коде.

В следующем разделе обсуждается более гибкий подход.

3. Using the *HttpMessageConverter *

В предыдущем разделе обсуждался базовый подход, который не использует возможностиMessage Conversion иContent Negotiation Spring MVC Framework. Для загрузки этой функции нам нужно:

  • Аннотируйте метод контроллера аннотацией@ResponseBody

  • Зарегистрируйте соответствующий преобразователь сообщений на основе типа возврата метода контроллера (например,ByteArrayHttpMessageConverter необходим для правильного преобразования массива байтов в файл изображения)

3.1. конфигурация

Для демонстрации конфигурации преобразователей мы будем использовать встроенныйByteArrayHttpMessageConverter, который преобразует сообщение всякий раз, когда метод возвращает типbyte[].

ByteArrayHttpMessageConverter зарегистрирован по умолчанию, но конфигурация аналогична любому другому встроенному или пользовательскому преобразователю.

Применение bean-компонента конвертера сообщений требует регистрации соответствующего bean-компонентаMessageConverter в контексте Spring MVC и настройки типов мультимедиа, которые он должен обрабатывать. Вы можете определить его через XML, используя тег<mvc:message-converters>.

Этот тег должен быть определен внутри тега<mvc:annotation-driven>, как в следующем примере:


    
        
            
                
                    image/jpeg
                    image/png
                
            
        
    

Вышеупомянутая часть конфигурации зарегистрируетByteArrayHttpMessageConverter для типов содержимого ответаimage/jpeg иimage/png. Если тег<mvc:message-converters> отсутствует в конфигурации mvc, то будет зарегистрирован набор преобразователей по умолчанию.

Также вы можете зарегистрировать конвертер сообщенийusing Java configuration:

@Override
public void configureMessageConverters(List> converters) {
    converters.add(byteArrayHttpMessageConverter());
}

@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
    ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
    arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
    return arrayHttpMessageConverter;
}

private List getSupportedMediaTypes() {
    List list = new ArrayList();
    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,, процесс преобразования легко настраивается, начиная от использования доступных преобразователей и заканчивая указанием пользовательского. Тип содержимого ответа не обязательно должен быть жестко закодирован, скорее это будетnegotiated на основе суффикса пути запроса.jpg.

Недостатком этого подхода является то, что вам необходимо явно реализовать логику для получения изображения из источника данных (локальный файл, внешнее хранилище и т. Д.), И вы не можете контролировать заголовки или код состояния ответа.

4. Использование классаResponseEntity

Вы можете вернуть изображение какbyte[], заключенное вResponse Entity. Spring MVCResponseEntity позволяет управлять не только телом HTTP-ответа, но также заголовком и кодом состояния отклика. Следуя этому подходу, вам необходимо определить возвращаемый тип метода какResponseEntity<byte[]> и создать возвращаемый объектResponseEntity в теле метода.

@RequestMapping(value = "/image-response-entity", method = RequestMethod.GET)
public ResponseEntity 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 responseEntity = new ResponseEntity<>(media, headers, HttpStatus.OK);
    return responseEntity;
}

ИспользованиеResponseEntity позволяет вам настроить код ответа для данного запроса.

Явная установка кода ответа особенно полезна перед лицом исключительного события, например, если изображение не было найдено (FileNotFoundException) или повреждено (IOException). В этих случаях все, что нужно, это установить код ответа, например, new ResponseEntity<>(null, headers, HttpStatus.NOT_FOUND), в соответствующем блоке улова.

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

5. Возврат изображения с использованием классаResource

Наконец, вы можете вернуть изображение в виде объектаResource.

Интерфейс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 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. Заключение

Среди вышеупомянутых подходов мы начали с базового подхода, а не с подхода, который использует функцию конвертации сообщений в платформе. Мы также обсудили, как получить код ответа и заголовки ответа, не передавая объект ответа напрямую.

Наконец, мы добавили гибкость с точки зрения местоположения изображения, потому что, где извлечь изображение, определяется в конфигурации, которую легче изменить на лету.

Пример кода после руководства доступен по адресуGitHub.