简体   繁体   English

如何将PNG图像从Jersey REST服务方法返回到浏览器

[英]How to return a PNG image from Jersey REST service method to the browser

I have a web server running with Jersey REST resources up and I wonder how to get an image/png reference for the browsers img tag; 我有一个运行Jersey REST资源的Web服务器,我想知道如何获取浏览器img标签的image / png参考; after submitting a Form or getting an Ajax response. 提交表单或获得Ajax响应后。 The image processing code for adding graphics is working, just need to return it somehow. 用于添加图形的图像处理代码正常工作,只需要以某种方式返回它。

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

}

Cheers 干杯

I'm not convinced its a good idea to return image data in a REST service. 我不相信在REST服务中返回图像数据是个好主意。 It ties up your application server's memory and IO bandwidth. 它占用了应用服务器的内存和IO带宽。 Much better to delegate that task to a proper web server that is optimized for this kind of transfer. 将该任务委派给适合此类传输的适当Web服务器要好得多。 You can accomplish this by sending a redirect to the image resource (as a HTTP 302 response with the URI of the image). 您可以通过向图像资源发送重定向来实现此目的(作为带有图像URI的HTTP 302响应)。 This assumes of course that your images are arranged as web content. 当然,这假定您的图像被安排为Web内容。

Having said that, if you decide you really need to transfer image data from a web service you can do so with the following (pseudo) code: 话虽如此,如果您确定需要从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();
}

Add in exception handling, etc etc. 添加异常处理等。

I built a general method for that with following features: 我为此构建了一个通用方法,具有以下功能:

  • returning "not modified" if the file hasn't been modified locally, a Status.NOT_MODIFIED is sent to the caller. 如果文件尚未在本地修改,则返回“未修改”,将Status.NOT_MODIFIED发送给调用者。 Uses Apache Commons Lang 使用Apache Commons Lang
  • using a file stream object instead of reading the file itself 使用文件流对象而不是读取文件本身

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

Note that the Locale switching does not seem to be thread-safe. 请注意,Locale切换似乎不是线程安全的。 I think, it's better to switch the locale globally. 我认为,最好全局切换语言环境。 I am not sure about the side-effects though... 我不确定副作用...

in regard of answer from @Perception, its true to be very memory-consuming when working with byte arrays, but you could also simply write back into the outputstream 关于来自@Perception的回答,它在使用字节数组时非常耗费内存,但你也可以简单地写回输出流

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

If you have a number of image resource methods, it is well worth creating a MessageBodyWriter to output the BufferedImage: 如果您有许多图像资源方法,那么创建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);
  } 
}

This MessageBodyWriter will be used automatically if auto-discovery is enabled for Jersey, otherwise it needs to be returned by a custom Application sub-class. 如果为Jersey启用了自动发现,则将自动使用此MessageBodyWriter,否则需要由自定义Application子类返回。 See JAX-RS Entity Providers for more info. 有关详细信息,请参阅JAX-RS实体提供程序

Once this is set up, simply return a BufferedImage from a resource method and it will be be output as image file data: 设置完成后,只需从资源方法返回一个BufferedImage,它将作为图像文件数据输出:

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

A couple of advantages to this approach: 这种方法有两个优点:

  • It writes to the response OutputSteam rather than an intermediary BufferedOutputStream 它写入响应OutputSteam而不是中间BufferedOutputStream
  • It supports both png and jpg output (depending on the media types allowed by the resource method) 它支持png和jpg输出(取决于资源方法允许的媒体类型)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM