Comment renvoyer une image PNG de la méthode de service REST Jersey au navigateur

J’ai un serveur web fonctionnant avec les ressources REST de Jersey et je me demande comment obtenir une référence image / png pour la balise img des navigateurs; après avoir soumis un formulaire ou obtenu une réponse Ajax. Le code de traitement d’image pour l’ajout de graphiques fonctionne, il suffit de le retourner.

Code:

@POST @Path("{fullsize}") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces("image/png") // Would need to replace void public void getFullImage(@FormDataParam("photo") InputStream imageIS, @FormDataParam("submit") Ssortingng extra) { BufferedImage image = ImageIO.read(imageIS); // .... image processing //.... image processing return ImageIO. .. ? } 

À votre santé

    Je ne suis pas convaincu que ce soit une bonne idée de renvoyer des données d’image dans un service REST. Il lie la mémoire de votre serveur d’applications et la bande passante d’E / S. Il vaut beaucoup mieux déléguer cette tâche à un serveur Web approprié, optimisé pour ce type de transfert. Vous pouvez accomplir cela en envoyant une redirection vers la ressource image (sous forme de réponse HTTP 302 avec l’URI de l’image). Cela suppose bien sûr que vos images soient organisées en contenu Web.

    Cela dit, si vous décidez de transférer des données d’image d’un service Web, vous pouvez le faire avec le (pseudo) code suivant:

     @Path("/whatever") @Produces("image/png") public Response getFullImage(...) { BufferedImage image = ...; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); byte[] imageData = baos.toByteArray(); // uncomment line below to send non-streamed // return Response.ok(imageData).build(); // uncomment line below to send streamed // return Response.ok(new ByteArrayInputStream(imageData)).build(); } 

    Ajouter à la gestion des exceptions, etc. etc.

    J’ai construit une méthode générale pour cela avec les fonctionnalités suivantes:

    • En retournant “non modifié” si le fichier n’a pas été modifié localement, un Status.NOT_MODIFIED est envoyé à l’appelant. Utilise Apache Commons Lang
    • utiliser un object de stream de fichiers au lieu de lire le fichier lui-même

    Voici le code:

     import org.apache.commons.lang3.time.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger logger = LoggerFactory.getLogger(Utils.class); @GET @Path("16x16") @Produces("image/png") public Response get16x16PNG(@HeaderParam("If-Modified-Since") Ssortingng modified) { File repositoryFile = new File("c:/temp/myfile.png"); return returnFile(repositoryFile, modified); } /** * * Sends the file if modified and "not modified" if not modified * future work may put each file with a unique id in a separate folder in tomcat * * use that static URL for each file * * if file is modified, URL of file changes * * -> client always fetches correct file * * method header for calling method public Response getXY(@HeaderParam("If-Modified-Since") Ssortingng modified) { * * @param file to send * @param modified - HeaderField "If-Modified-Since" - may be "null" * @return Response to be sent to the client */ public static Response returnFile(File file, Ssortingng modified) { if (!file.exists()) { return Response.status(Status.NOT_FOUND).build(); } // do we really need to send the file or can send "not modified"? if (modified != null) { Date modifiedDate = null; // we have to switch the locale to ENGLISH as parseDate parses in the default locale Locale old = Locale.getDefault(); Locale.setDefault(Locale.ENGLISH); try { modifiedDate = DateUtils.parseDate(modified, org.apache.http.impl.cookie.DateUtils.DEFAULT_PATTERNS); } catch (ParseException e) { logger.error(e.getMessage(), e); } Locale.setDefault(old); if (modifiedDate != null) { // modifiedDate does not carry milliseconds, but fileDate does // therefore we have to do a range-based comparison // 1000 milliseconds = 1 second if (file.lastModified()-modifiedDate.getTime() < DateUtils.MILLIS_PER_SECOND) { return Response.status(Status.NOT_MODIFIED).build(); } } } // we really need to send the file try { Date fileDate = new Date(file.lastModified()); return Response.ok(new FileInputStream(file)).lastModified(fileDate).build(); } catch (FileNotFoundException e) { return Response.status(Status.NOT_FOUND).build(); } } /*** copied from org.apache.http.impl.cookie.DateUtils, Apache 2.0 License ***/ /** * Date format pattern used to parse HTTP date headers in RFC 1123 format. */ public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz"; /** * Date format pattern used to parse HTTP date headers in RFC 1036 format. */ public static final String PATTERN_RFC1036 = "EEEE, dd-MMM-yy HH:mm:ss zzz"; /** * Date format pattern used to parse HTTP date headers in ANSI C * asctime() format. */ public static final Ssortingng PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy"; public static final Ssortingng[] DEFAULT_PATTERNS = new Ssortingng[] { PATTERN_RFC1036, PATTERN_RFC1123, PATTERN_ASCTIME }; 

    Notez que la commutation de parameters régionaux ne semble pas être compatible avec les threads. Je pense qu’il est préférable de changer les parameters régionaux globalement. Je ne suis pas sûr des effets secondaires cependant …

    En ce qui concerne la réponse de @Perception, il est vrai qu’il consum beaucoup de mémoire lorsque vous travaillez avec des tableaux d’octets, mais vous pouvez également simplement réécrire dans le stream de sortie.

     @Path("/picture") public class ProfilePicture { @GET @Path("/thumbnail") @Produces("image/png") public StreamingOutput getThumbNail() { return new StreamingOutput() { @Override public void write(OutputStream os) throws IOException, WebApplicationException { //... read your stream and write into os } }; } } 

    Si vous avez un certain nombre de méthodes de ressources d’image, il est préférable de créer un MessageBodyWriter pour générer le BufferedImage:

     @Produces({ "image/png", "image/jpg" }) @Provider public class BufferedImageBodyWriter implements MessageBodyWriter { @Override public boolean isWriteable(Class type, Type type1, Annotation[] antns, MediaType mt) { return type == BufferedImage.class; } @Override public long getSize(BufferedImage t, Class type, Type type1, Annotation[] antns, MediaType mt) { return -1; // not used in JAX-RS 2 } @Override public void writeTo(BufferedImage image, Class type, Type type1, Annotation[] antns, MediaType mt, MultivaluedMap mm, OutputStream out) throws IOException, WebApplicationException { ImageIO.write(image, mt.getSubtype(), out); } } 

    Ce MessageBodyWriter sera utilisé automatiquement si la détection automatique est activée pour Jersey, sinon elle doit être renvoyée par une sous-classe Application personnalisée. Voir Fournisseurs d’entités JAX-RS pour plus d’informations.

    Une fois que ceci est configuré, retournez simplement un BufferedImage à partir d’une méthode de ressource et celle-ci sera sortie en tant que données de fichier image:

     @Path("/whatever") @Produces({"image/png", "image/jpg"}) public Response getFullImage(...) { BufferedImage image = ...; return Response.ok(image).build(); } 

    Quelques avantages à cette approche:

    • Il écrit dans la réponse OutputSteam plutôt qu’un intermédiaire BufferedOutputStream
    • Il prend en charge les sorties png et jpg (en fonction des types de supports autorisés par la méthode de ressource)