如何从泽西REST服务方法返回一个PNG图像到浏览器

我有一个运行Jersey REST资源的Web服务器,我不知道如何获得浏览器img标签的图像/ PNG参考; 提交表单或获得Ajax响应后。 添加graphics的image processing代码正在工作,只需要以某种方式返回它。

码:

@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. .. ? } 

干杯

我不相信在REST服务中返回图像数据的好主意。 它将您的应用程序服务器的内存和IO带宽绑定在一起。 把这个任务委托给一个适合这种传输优化的适当的Web服务器会更好。 您可以通过向图像资源发送redirect来完成此操作(作为具有图像URI的HTTP 302响应)。 这当然假设您的图像被安排为网页内容。

话虽如此,如果您决定确实需要从Web服务传输图像数据,则可以使用以下(伪)代码进行传输:

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

joinexception处理等

我build立了以下function的一般方法:

  • 如果文件尚未在本地修改,则返回“未修改”,则将Status.NOT_MODIFIED发送给调用者。 使用Apache Commons Lang
  • 使用文件stream对象而不是读取文件本身

这里代码:

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

请注意,区域设置切换似乎不是线程安全的。 我认为,最好全局切换语言环境。 虽然我不确定副作用

关于来自@Perception的回答,在使用字节数组的时候,它的真正意义是非常消耗内存,但是你也可以简单地写回输出stream

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

如果你有一些图像资源方法,创build一个MessageBodyWriter来输出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); } } 

如果启用了Jersey的自动发现function,则会自动使用此MessageBodyWriter,否则需要由自定义应用程序子类返回。 有关更多信息,请参阅JAX-RS实体提供程序 。

一旦build立,只需从资源方法中返回一个BufferedImage,它将被输出为图像文件数据:

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

这种方法有两个好处:

  • 它写入响应OutputSteam而不是中间BufferedOutputStream
  • 它支持png和jpg输出(取决于资源方法允许的媒体types)