J'ai un serveur Web fonctionnant avec Jersey REST ressources 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 traitement de l'image le code pour l’ajout de graphiques fonctionne, il suffit de le retourner d’une manière ou d’une autre.
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") String 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 les données d'image dans un service REST. Cela bloque la mémoire de votre serveur d'applications et IO la bande passante. déléguez cette tâche à un serveur Web approprié optimisé pour ce type de transfert (vous pouvez y parvenir en envoyant une redirection vers la ressource image (sous la forme d'une réponse HTTP 302 avec l'URI de l'image). Cela suppose bien entendu que vos images sont organisés en tant que contenu Web.
Cela dit, si vous décidez que vous devez réellement transférer des données d'image à partir d'un service Web, vous pouvez le faire avec le code (pseudo) 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 dans la gestion des exceptions, etc. etc.
J'ai construit une méthode générale pour cela avec les fonctionnalités suivantes:
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") String 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") String 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, String 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
* <code>asctime()</code> format.
*/
public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
public static final String[] DEFAULT_PATTERNS = new String[] {
PATTERN_RFC1036,
PATTERN_RFC1123,
PATTERN_ASCTIME
};
Notez que le changement de paramètres régionaux ne semble pas être thread-safe. Je pense qu'il est préférable de changer les paramètres régionaux globalement. Je ne suis toutefois pas sûr des effets secondaires ...
en ce qui concerne la réponse de @Perception, il est vrai que vous devez utiliser beaucoup de mémoire lorsque vous travaillez avec des tableaux d'octets, mais vous pouvez aussi simplement écrire dans le flux 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 plusieurs méthodes de ressources image, il est utile de créer un MessageBodyWriter pour générer BufferedImage:
@Produces({ "image/png", "image/jpg" })
@Provider
public class BufferedImageBodyWriter implements MessageBodyWriter<BufferedImage> {
@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<String, Object> 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, il doit être renvoyé par une sous-classe Application personnalisée. Voir Fournisseurs d'entités JAX-RS pour plus d'informations.
Une fois que cela est configuré, renvoyez simplement une BufferedImage à partir d'une méthode de ressource et elle 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 de cette approche: