Retourner des données image/média avec Spring MVC

Renvoi de données image / média avec Spring MVC

1. Vue d'ensemble

Dans ce didacticiel, nous allons illustrer comment renvoyer des images et d'autres médias à l'aide du framework Spring MVC.

Nous discuterons de plusieurs approches, en commençant par manipuler directementHttpServletResponse plutôt qu'en passant à des approches qui bénéficient de l'abstractionMessage Conversion,Content Negotiation et SpringResource. Nous examinerons chacun d’entre eux de plus près et discuterons de leurs avantages et inconvénients.

2. Utilisation desHttpServletResponse

L'approche la plus basique du téléchargement d'image consiste à travailler directement sur un objetresponse et à imiter une implémentation pure deServlet, et cela est démontré à l'aide de l'extrait de code suivant:

@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'émission de la requête suivante rendra l'image dans un navigateur:

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

L'implémentation est assez directe et simple grâce àIOUtils du packageorg.apache.commons.io. Cependant, l’inconvénient de cette approche est qu’elle n’est pas robuste face aux changements potentiels. Le type mime est codé en dur et le changement de la logique de conversion ou l'externalisation de l'emplacement de l'image nécessitent des modifications du code.

La section suivante aborde une approche plus flexible.

3. Using the *HttpMessageConverter *

La section précédente a abordé une approche de base qui ne tire pas parti des fonctionnalitésMessage Conversion etContent Negotiation de Spring MVC Framework. Pour amorcer ces fonctionnalités, nous devons:

  • Annoter la méthode du contrôleur avec l'annotation@ResponseBody

  • Enregistrez un convertisseur de message approprié en fonction du type de retour de la méthode du contrôleur (ByteArrayHttpMessageConverter par exemple nécessaire pour une conversion correcte du tableau d'octets en fichier image)

3.1. Configuration

Pour présenter la configuration des convertisseurs, nous utiliserons leByteArrayHttpMessageConverter intégré qui convertit un message chaque fois qu'une méthode renvoie le typebyte[].

LeByteArrayHttpMessageConverter est enregistré par défaut, mais la configuration est analogue à tout autre convertisseur intégré ou personnalisé.

L'application du bean convertisseur de message nécessite l'enregistrement d'un beanMessageConverter approprié dans le contexte Spring MVC et la configuration des types de support qu'il doit gérer. Vous pouvez le définir via XML, en utilisant la balise<mvc:message-converters>.

Cette balise doit être définie dans la balise<mvc:annotation-driven>, comme dans l'exemple suivant:


    
        
            
                
                    image/jpeg
                    image/png
                
            
        
    

La partie de configuration mentionnée ci-dessus enregistreraByteArrayHttpMessageConverter pour les types de contenu de réponseimage/jpeg etimage/png. Si la balise<mvc:message-converters> n'est pas présente dans la configuration mvc, alors le jeu de convertisseurs par défaut sera enregistré.

En outre, vous pouvez enregistrer le convertisseur de messagesusing 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. la mise en oeuvre

Maintenant, nous pouvons implémenter notre méthode qui gérera les demandes de média. Comme mentionné ci-dessus, vous devez marquer votre méthode de contrôleur avec l'annotation@ResponseBody et utiliserbyte[] comme type de retour:

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

Pour tester la méthode, lancez la requête suivante dans votre navigateur:

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

Du côté des avantages, la méthode ne sait rien desHttpServletResponse,, le processus de conversion est hautement configurable, allant de l'utilisation des convertisseurs disponibles à la spécification d'un convertisseur personnalisé. Le type de contenu de la réponse n'a pas besoin d'être codé en dur mais seranegotiated basé sur le suffixe de chemin de requête.jpg.

L'inconvénient de cette approche est que vous devez implémenter explicitement la logique de récupération de l'image à partir d'une source de données (fichier local, stockage externe, etc.) et que vous n'avez pas de contrôle sur les en-têtes ou le code d'état de la réponse.

4. Utilisation de la classeResponseEntity

Vous pouvez renvoyer une image en tant quebyte[] enveloppée dans lesResponse Entity. Spring MVCResponseEntity permet de contrôler non seulement le corps de la réponse HTTP, mais également l'en-tête et le code d'état de la réponse. En suivant cette approche, vous devez définir le type de retour de la méthode en tant queResponseEntity<byte[]> et créer un objetResponseEntity retournant dans le corps de la méthode.

@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'utilisation desResponseEntity vous permet de configurer un code de réponse pour une requête donnée.

Définir explicitement le code de réponse est particulièrement utile face à un événement exceptionnel, par ex. si l'image n'a pas été trouvée (FileNotFoundException) ou est corrompue (IOException). Dans ces cas, il suffit de définir le code de réponse, par exemple. new ResponseEntity<>(null, headers, HttpStatus.NOT_FOUND), dans un bloc catch adéquat.

De plus, si vous devez définir des en-têtes spécifiques dans votre réponse, cette approche est plus simple que la définition des en-têtes au moyen de l'objetHttpServletResponse accepté par la méthode comme paramètre. Cela rend la signature de la méthode claire et ciblée.

5. Renvoi d'une image à l'aide de la classeResource

Enfin, vous pouvez renvoyer une image sous la forme de l'objetResource.

L'interfaceResource est une interface permettant d'abstraire l'accès aux ressources de bas niveau. Il est introduit dans Spring comme un remplacement plus performant de la classe standardjava.net.URL. Il permet un accès facile à différents types de ressources (fichiers locaux, fichiers distants, ressources de chemin de classe) sans qu'il soit nécessaire d'écrire un code qui les récupère explicitement.

Pour utiliser cette approche, le type de retour de la méthode doit être défini surResource et vous devez annoter la méthode avec l'annotation@ResponseBody.

5.1. la mise en oeuvre

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

ou, si nous voulons plus de contrôle sur les en-têtes de réponse:

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

En utilisant cette approche, vous traitez les images comme des ressources qui peuvent être chargées à l'aide de l'implémentation de l'interfaceResourceLoader. Dans ce cas, vous faites abstraction de l'emplacement exact de votre image etResourceLoader décide d'où elle est chargée.

Il fournit une approche commune pour contrôler l'emplacement des images à l'aide de la configuration et élimine le besoin d'écrire du code de chargement de fichier.

6. Conclusion

Parmi les approches susmentionnées, nous avons commencé par l’approche de base, puis par l’utilisation de l’approche qui bénéficie de la fonctionnalité de conversion des messages du cadre. Nous avons également discuté de la façon d'obtenir l'ensemble le code de réponse et les en-têtes de réponse sans traiter directement l'objet de réponse.

Enfin, nous avons ajouté de la flexibilité du point de vue des emplacements d’image, car l’emplacement de récupération d’une image est défini dans la configuration, qui est plus facile à modifier à la volée.

L'exemple de code suivant le didacticiel est disponible àGitHub.