Jak zwrócić obraz PNG z usługi Jersey REST do przeglądarki
Mam serwer WWW uruchomiony z rest resources up I zastanawiam się, jak uzyskać odniesienie image / png dla znacznika img przeglądarki; po wysłaniu formularza lub uzyskanie odpowiedzi Ajax. Kod przetwarzania obrazu do dodawania Grafiki działa, Wystarczy go jakoś zwrócić.
Kod:
@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. .. ?
}
Cheers
4 answers
Nie jestem przekonany, że to dobry pomysł, aby zwrócić dane obrazu w usłudze odpoczynku. Łączy pamięć serwera aplikacji i przepustowość IO. Znacznie lepiej delegować to zadanie na odpowiedni serwer WWW, który jest zoptymalizowany do tego rodzaju transferu. Można to osiągnąć wysyłając przekierowanie do zasobu obrazu (jako odpowiedź HTTP 302 z URI obrazu). Zakłada to oczywiście, że obrazy są ułożone jako zawartość internetowa.
To powiedziawszy, jeśli zdecydujesz, że naprawdę musisz transfer danych obrazu z serwisu internetowego można to zrobić za pomocą następującego (pseudo) kodu:@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();
}
Dodaj obsługę wyjątków, itd itd.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-09 03:01:09
Zbudowałem dla tego ogólną metodę z następującymi cechami:
- zwracanie "not modified" jeśli plik nie był modyfikowany lokalnie, Status.NOT_MODIFIED jest wysyłane do wywołującego. Uses Apache Commons Lang
- używanie obiektu strumienia plików zamiast odczytywania samego pliku
Tutaj kod:
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
};
Zauważ, że przełączanie ustawień regionalnych nie wydaje się bezpieczne dla wątków. Myślę, że lepiej zmienić lokalizację globalnie. Nie jestem pewien co do skutki uboczne...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-01 13:55:50
W odniesieniu do odpowiedzi z @ Perception, to prawda, że jest bardzo pochłaniająca pamięć podczas pracy z tablicami bajtów, ale można również po prostu napisać z powrotem do outputstream
@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
}
};
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-06 17:44:01
Jeśli masz kilka metod zasobów obrazu, warto utworzyć MessageBodyWriter, aby wyświetlić 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);
}
}
Ten MessageBodyWriter zostanie użyty automatycznie, jeśli auto-discovery jest włączone dla Jersey, w przeciwnym razie musi zostać zwrócony przez podklasę aplikacji niestandardowej. Zobacz JAX-RS Entity Providers aby uzyskać więcej informacji.
Po skonfigurowaniu, po prostu zwróć BufferedImage z metody zasobów, a zostanie on wyświetlony jako plik obrazu data:
@Path("/whatever")
@Produces({"image/png", "image/jpg"})
public Response getFullImage(...) {
BufferedImage image = ...;
return Response.ok(image).build();
}
Kilka zalet tego podejścia:
- Zapisuje do wyjścia odpowiedzi, a nie pośredniego Buforedoutputstream
Obsługuje zarówno format png, jak i jpg (w zależności od typów nośników dozwolonych przez metodę zasobów) [14]}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-24 06:47:44