Retornando dados de imagem / mídia com o Spring MVC
1. Visão geral
Neste tutorial, ilustraremos como retornar imagens e outras mídias usando a estrutura Spring MVC.
Discutiremos várias abordagens, começando pela manipulação direta deHttpServletResponse do que passando para abordagens que se beneficiam deMessage Conversion,Content Negotiatione abstraçãoResource de Spring. Vamos dar uma olhada em cada um deles e discutir suas vantagens e desvantagens.
2. Usando oHttpServletResponse
A abordagem mais básica do download da imagem é trabalhar diretamente contra um objetoresponse e simular uma implementaçãoServlet pura, e é demonstrada usando o seguinte snippet:
@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());
}
A emissão da solicitação a seguir renderizará a imagem em um navegador:
http://localhost:8080/spring-mvc-xml/image-manual-response.jpg
A implementação é bastante direta e simples devido aIOUtils do pacoteorg.apache.commons.io. No entanto, a desvantagem da abordagem é que ela não é robusta contra as possíveis mudanças. O tipo MIME é codificado e a alteração da lógica de conversão ou a externalização do local da imagem exigem alterações no código.
A seção a seguir discute uma abordagem mais flexível.
3. Using the *HttpMessageConverter *
A seção anterior discutiu uma abordagem básica que não tira proveito dos recursosMessage ConversioneContent Negotiation do Spring MVC Framework. Para inicializar esses recursos, precisamos:
-
Anote o método do controlador com a anotação@ResponseBody
-
Registre um conversor de mensagem apropriado com base no tipo de retorno do método do controlador (ByteArrayHttpMessageConverter, por exemplo, necessário para a conversão correta da matriz de bytes em um arquivo de imagem)
3.1. Configuração
Para mostrar a configuração dos conversores, usaremos oByteArrayHttpMessageConverter embutido que converte uma mensagem sempre que um método retorna o tipobyte[].
OByteArrayHttpMessageConverter é registrado por padrão, mas a configuração é análoga para qualquer outro conversor embutido ou personalizado.
Aplicar o bean conversor de mensagem requer o registro de um beanMessageConverter apropriado dentro do contexto Spring MVC e a configuração dos tipos de mídia que ele deve manipular. Você pode defini-lo via XML, usando a tag<mvc:message-converters>.
Esta tag deve ser definida dentro da tag<mvc:annotation-driven>, como no exemplo a seguir:
image/jpeg
image/png
A parte de configuração mencionada anteriormente registraráByteArrayHttpMessageConverter para os tipos de conteúdo de respostaimage/jpegeimage/png. Se a tag<mvc:message-converters> não estiver presente na configuração do mvc, o conjunto padrão de conversores será registrado.
Além disso, você pode registrar o conversor de mensagemusing 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. Implementação
Agora podemos implementar nosso método que manipulará solicitações de mídia. Como foi mencionado acima, você precisa marcar seu método de controlador com a anotação@ResponseBody e usarbyte[] como o tipo de retorno:
@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);
}
Para testar o método, emita a seguinte solicitação no seu navegador:
http://localhost:8080/spring-mvc-xml/image-byte-array.jpg
No lado da vantagem, o método não sabe nada sobre oHttpServletResponse, o processo de conversão é altamente configurável, variando de usar os conversores disponíveis para especificar um personalizado. O tipo de conteúdo da resposta não precisa ser codificado permanentemente, em vez disso, seránegotiated com base no sufixo do caminho de solicitação.jpg.
A desvantagem dessa abordagem é que você precisa implementar explicitamente a lógica para recuperar a imagem de uma fonte de dados (arquivo local, armazenamento externo, etc.) e você não tem controle sobre os cabeçalhos ou o código de status da resposta.
4. Usando a classeResponseEntity
Você pode retornar uma imagem comobyte[] incluída emResponse Entity. Spring MVCResponseEntity permite o controle não apenas sobre o corpo da resposta HTTP, mas também o cabeçalho e o código de status respose. Seguindo essa abordagem, você precisa definir o tipo de retorno do método comoResponseEntity<byte[]>e criar o objetoResponseEntity de retorno no corpo do método.
@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;
}
Usar oResponseEntity permite que você configure um código de resposta para uma determinada solicitação.
A configuração explícita do código de resposta é especialmente útil em face de um evento excepcional, por exemplo se a imagem não foi encontrada (FileNotFoundException) ou está corrompida (IOException). Nesses casos, tudo o que é necessário é definir o código de resposta, por exemplo. new ResponseEntity<>(null, headers, HttpStatus.NOT_FOUND), em um bloco catch adequado.
Além disso, se você precisar definir alguns cabeçalhos específicos em sua resposta, essa abordagem é mais direta do que definir cabeçalhos por meio do objetoHttpServletResponse que é aceito pelo método como um parâmetro. Isso torna a assinatura do método clara e focada.
5. Retornando imagem usando a classeResource
Finalmente, você pode retornar uma imagem na forma do objetoResource.
A interfaceResource é uma interface para abstrair o acesso a recursos de baixo nível. Ele é apresentado no Spring como um substituto mais capaz para a classejava.net.URL padrão. Permite um acesso fácil a diferentes tipos de recursos (arquivos locais, arquivos remotos, recursos do caminho de classe) sem a necessidade de escrever um código que os recupere explicitamente.
Para usar esta abordagem, o tipo de retorno do método deve ser definido comoResourcee você precisa anotar o método com a anotação@ResponseBody.
5.1. Implementação
@ResponseBody
@RequestMapping(value = "/image-resource", method = RequestMethod.GET)
public Resource getImageAsResource() {
return new ServletContextResource(servletContext, "/WEB-INF/images/image-example.jpg");
}
ou, se quisermos ter mais controle sobre os cabeçalhos de resposta:
@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);
}
Usando essa abordagem, você trata imagens como recursos que podem ser carregados usando a implementação de interfaceResourceLoader. Nesse caso, você abstrai da localização exata de sua imagem eResourceLoader decide de onde ela será carregada.
Ele fornece uma abordagem comum para controlar o local das imagens usando a configuração e eliminar a necessidade de gravar o código de carregamento de arquivos.
6. Conclusão
Entre as abordagens mencionadas acima, partimos da abordagem básica, do que usar a abordagem que se beneficia do recurso de conversão de mensagens da estrutura. Também discutimos como definir o código de resposta e os cabeçalhos de resposta sem entregar diretamente o objeto de resposta.
Por fim, adicionamos flexibilidade do ponto de vista dos locais da imagem, porque de onde recuperar uma imagem é definida na configuração que é mais fácil de alterar em tempo real.
O código de amostra após o tutorial está disponível emGitHub.