Restituzione di dati immagine / media con Spring MVC

1. Panoramica

In questo tutorial, illustreremo come restituire immagini e altri media utilizzando il framework Spring MVC.

Discuteremo diversi approcci, a partire dalla manipolazione diretta di HttpServletResponse per passare ad approcci che traggono vantaggio dalla conversione dei messaggi, dalla negoziazione del contenuto e dall'astrazione delle risorse di Spring . Daremo uno sguardo più da vicino a ciascuno di essi e ne discuteremo i vantaggi e gli svantaggi.

2. Utilizzo di HttpServletResponse

L'approccio più basilare del download dell'immagine consiste nel lavorare direttamente con un oggetto di risposta e imitare un'implementazione pura di Servlet , e viene dimostrato utilizzando il seguente frammento:

@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()); }

L'emissione della seguente richiesta renderà l'immagine in un browser:

//localhost:8080/spring-mvc-xml/image-manual-response.jpg

L'implementazione è abbastanza diretta e semplice grazie agli IOUtils del pacchetto org.apache.commons.io . Tuttavia, lo svantaggio dell'approccio è che non è robusto contro i potenziali cambiamenti. Il tipo MIME è hardcoded e la modifica della logica di conversione o l'esternalizzazione della posizione dell'immagine richiede modifiche al codice.

La sezione seguente illustra un approccio più flessibile.

3. Utilizzo di HttpMessageConverter

La sezione precedente ha discusso un approccio di base che non sfrutta le funzionalità di conversione dei messaggi e negoziazione dei contenuti di Spring MVC Framework. Per eseguire il bootstrap di queste funzionalità dobbiamo:

  • Annota il metodo del controller con l' annotazione @ResponseBody
  • Registrare un convertitore di messaggi appropriato in base al tipo di ritorno del metodo del controller ( ByteArrayHttpMessageConverter ad esempio necessario per la corretta conversione dell'array di byte in un file immagine)

3.1. Configurazione

Per mostrare la configurazione dei convertitori, useremo il ByteArrayHttpMessageConverter incorporato che converte un messaggio ogni volta che un metodo restituisce il tipo byte [] .

Il ByteArrayHttpMessageConverter è inserito di default, ma la configurazione è analogo per qualsiasi altro incorporato o convertitore personalizzato.

L'applicazione del bean di conversione messaggi richiede la registrazione di un bean MessageConverter appropriato all'interno del contesto Spring MVC e l'impostazione dei tipi di supporto che dovrebbe gestire. Puoi definirlo tramite XML, usandoetichetta.

Questo tag dovrebbe essere definito all'interno tag, come nell'esempio seguente:

     image/jpeg image/png     

La parte di configurazione di cui sopra registrerà ByteArrayHttpMessageConverter per i tipi di contenuto di risposta immagine / jpeg e immagine / png . Se tag non è presente nella configurazione mvc, quindi verrà registrato il set predefinito di convertitori.

Inoltre, puoi registrare il convertitore di messaggi utilizzando la configurazione Java :

@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. Implementazione

Ora possiamo implementare il nostro metodo che gestirà le richieste per i media. Come accennato in precedenza, è necessario contrassegnare il metodo del controller con l' annotazione @ResponseBody e utilizzare byte [] come tipo di ritorno:

@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); }

Per testare il metodo, invia la seguente richiesta nel tuo browser:

//localhost:8080/spring-mvc-xml/image-byte-array.jpg

Sul lato vantaggio, il metodo non sa nulla di HttpServletResponse, il processo di conversione è altamente configurabile, che va dall'uso dei convertitori disponibili alla specifica di uno personalizzato. Il tipo di contenuto della risposta non deve essere codificato in modo rigido, ma verrà negoziato in base al suffisso del percorso della richiesta .jpg .

Lo svantaggio di questo approccio è che è necessario implementare in modo esplicito la logica per il recupero dell'immagine da un'origine dati (file locale, archiviazione esterna, ecc.) E non si ha il controllo sulle intestazioni o sul codice di stato della risposta.

4. Utilizzo della classe ResponseEntity

È possibile restituire un'immagine come byte [] racchiusa in Response Entity . Spring MVC ResponseEntity consente il controllo non solo sul corpo della risposta HTTP ma anche sull'intestazione e sul codice di stato della risposta. Seguendo questo approccio, è necessario definire il tipo restituito del metodo come ResponseEntity e creare l' oggetto ResponseEntity restituito nel corpo del metodo.

@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; }

L'utilizzo di ResponseEntity consente di configurare un codice di risposta per una determinata richiesta.

L'impostazione esplicita del codice di risposta è particolarmente utile di fronte a un evento eccezionale, ad esempio se l'immagine non è stata trovata ( FileNotFoundException ) o è danneggiata ( IOException) . In questi casi, tutto ciò che serve è impostare il codice di risposta, ad esempio new ResponseEntity (null, headers, HttpStatus.NOT_FOUND), in un blocco catch adeguato.

Inoltre, se è necessario impostare alcune intestazioni specifiche nella risposta, questo approccio è più semplice rispetto all'impostazione delle intestazioni tramite l' oggetto HttpServletResponse accettato dal metodo come parametro. Rende la firma del metodo chiara e mirata.

5. Restituzione dell'immagine utilizzando la classe di risorse

Infine, puoi restituire un'immagine sotto forma di oggetto Risorsa .

L' interfaccia delle risorse è un'interfaccia per astrarre l'accesso alle risorse di basso livello. Viene introdotto in primavera come un sostituto più efficiente per la classe java.net.URL standard . Consente un facile accesso a diversi tipi di risorse (file locali, file remoti, risorse del percorso di classe) senza la necessità di scrivere un codice che le recuperi esplicitamente.

Per utilizzare questo approccio, il tipo di ritorno del metodo dovrebbe essere impostato su Risorsa ed è necessario annotare il metodo con l' annotazione @ResponseBody .

5.1. Implementazione

@ResponseBody @RequestMapping(value = "/image-resource", method = RequestMethod.GET) public Resource getImageAsResource() { return new ServletContextResource(servletContext, "/WEB-INF/images/image-example.jpg"); }

oppure, se vogliamo un maggiore controllo sulle intestazioni di risposta:

@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 questo approccio, tratti le immagini come risorse che possono essere caricate usando l' implementazione dell'interfaccia ResourceLoader . In tal caso, astratti dalla posizione esatta dell'immagine e ResourceLoader decide da dove viene caricata.

Fornisce un approccio comune per controllare la posizione delle immagini utilizzando la configurazione ed elimina la necessità di scrivere codice di caricamento dei file.

6. Conclusione

Tra gli approcci sopra citati, siamo partiti dall'approccio di base, quindi utilizzando l'approccio che beneficia della funzionalità di conversione dei messaggi del framework. Abbiamo anche discusso come ottenere l'insieme del codice di risposta e delle intestazioni di risposta senza consegnare direttamente l'oggetto risposta.

Infine, abbiamo aggiunto flessibilità dal punto di vista delle posizioni delle immagini, perché da dove recuperare un'immagine è definito nella configurazione che è più facile da cambiare al volo.

Scarica un'immagine o un file con Spring spiega come ottenere la stessa cosa utilizzando Spring Boot.

Il codice di esempio che segue il tutorial è disponibile su GitHub.