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; after submitting a Form or getting an Ajax response. The image processing code for adding graphics is working, just need to return it somehow.
我有一个运行Jersey REST资源的Web服务器,我想知道如何获取浏览器img标签的image / png参考;提交表单或获得Ajax响应后。用于添加图形的图像处理代码正常工作,只需要以某种方式返回它。
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
干杯
4 个解决方案
#1
83
I'm not convinced its a good idea to return image data in a REST service. It ties up your application server's memory and IO bandwidth. Much better to delegate that task to a proper web server that is optimized for this kind of transfer. You can accomplish this by sending a redirect to the image resource (as a HTTP 302 response with the URI of the image). This assumes of course that your images are arranged as web content.
我不相信在REST服务中返回图像数据是个好主意。它占用了应用服务器的内存和IO带宽。将该任务委派给适合此类传输的适当Web服务器要好得多。您可以通过向图像资源发送重定向来实现此目的(作为带有图像URI的HTTP 302响应)。当然,这假定您的图像被安排为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.
添加异常处理等。
#2
12
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. Uses Apache Commons Lang
- 如果文件尚未在本地修改,则返回“未修改”,将Status.NOT_MODIFIED发送给调用者。使用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. I think, it's better to switch the locale globally. I am not sure about the side-effects though...
请注意,Locale切换似乎不是线程安全的。我认为,最好全局切换语言环境。我不确定副作用...
#3
6
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
}
};
}
}
#4
3
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. See JAX-RS Entity Providers for more info.
如果为Jersey启用了自动发现,则将自动使用此MessageBodyWriter,否则需要由自定义Application子类返回。有关详细信息,请参阅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输出(取决于资源方法允许的媒体类型)
#1
83
I'm not convinced its a good idea to return image data in a REST service. It ties up your application server's memory and IO bandwidth. Much better to delegate that task to a proper web server that is optimized for this kind of transfer. You can accomplish this by sending a redirect to the image resource (as a HTTP 302 response with the URI of the image). This assumes of course that your images are arranged as web content.
我不相信在REST服务中返回图像数据是个好主意。它占用了应用服务器的内存和IO带宽。将该任务委派给适合此类传输的适当Web服务器要好得多。您可以通过向图像资源发送重定向来实现此目的(作为带有图像URI的HTTP 302响应)。当然,这假定您的图像被安排为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.
添加异常处理等。
#2
12
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. Uses Apache Commons Lang
- 如果文件尚未在本地修改,则返回“未修改”,将Status.NOT_MODIFIED发送给调用者。使用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. I think, it's better to switch the locale globally. I am not sure about the side-effects though...
请注意,Locale切换似乎不是线程安全的。我认为,最好全局切换语言环境。我不确定副作用...
#3
6
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
}
};
}
}
#4
3
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. See JAX-RS Entity Providers for more info.
如果为Jersey启用了自动发现,则将自动使用此MessageBodyWriter,否则需要由自定义Application子类返回。有关详细信息,请参阅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输出(取决于资源方法允许的媒体类型)