11

I'm writing an image to servlet response with best performance. Any advices, practices, experience?

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
tabdulin
  • 353
  • 2
  • 4
  • 14
  • I save images in DB in current project. Specific spring controllers gets images' byte arrays from DB and write them to servlet response. Also there's gonna be a web server in front of servlet container. As I know it may lead to some performance issues. Right now I don't have troubles, but wish to know if there are some good solutions for the case. E.g. some caching practices, specific headers, etc. Some experience about DB vs FileSystem as image store are appreciated. Storing images in database seems to me more comfortable. – tabdulin Jun 05 '10 at 11:41

3 Answers3

23

For best performance and efficiency, don't put the entire content in byte[]. Each byte eats, yes, one byte from Java's memory. Imagine 100 concurrent users which requests 10 images of each 100KB, that's already 100MB of Java memory eaten away.

Get the image as an InputStream from the DB using ResultSet#getBinaryStream(), wrap it in an BufferedInputStream and write it to the OutputStream of the response wrapped in an BufferedOutputStream through a small byte[] buffer.

Assuming that you select images by the database key as identifier, use this in your HTML:

<img src="images/123">

Create a Servlet class which is mapped in web.xml on an url-pattern of /images/* and implement its doGet() method as follows.:

Long imageId = Long.valueOf(request.getPathInfo().substring(1)); // 123
Image image = imageDAO.find(imageId); // Get Image from DB.
// Image class is just a Javabean with the following properties:
// private String filename;
// private Long length;
// private InputStream content;

response.setHeader("Content-Type", getServletContext().getMimeType(image.getFilename()));
response.setHeader("Content-Length", String.valueOf(image.getLength()));
response.setHeader("Content-Disposition", "inline; filename=\"" + image.getFilename() + "\"");

BufferedInputStream input = null;
BufferedOutputStream output = null;

try {
    input = new BufferedInputStream(image.getContent());
    output = new BufferedOutputStream(response.getOutputStream());
    byte[] buffer = new byte[8192];
    for (int length = 0; (length = input.read(buffer)) > 0) {
        output.write(buffer, 0, length);
    }
} finally {
    if (output != null) try { output.close(); } catch (IOException logOrIgnore) {}
    if (input != null) try { input.close(); } catch (IOException logOrIgnore) {}
}

In the ImageDAO#find() you can use ResultSet#getBinaryStream() to get the image as an InputStream from the database.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • a minor variation on this may be to allow the ImageDAO to write the response back or maybe use a callback like style (think spring JdbcTemplate or HibernateTemplate). Otherwise the JDBCConnection and ResultSet will have to be left open after returning from ImageDAO.find() in order for the Image#content InputStream to work. – Gareth Davis Jun 05 '10 at 14:12
  • Hi, while you say "do not use byte[]" you use `byte[] buffer = new byte[8192];`, then what is the point? – hguser Jul 09 '14 at 02:16
  • @hguser: I clarified the answer. – BalusC Jul 09 '14 at 05:18
  • @BalusC: Thank you! Then I wonder what is the better practice if my application should cache the `BufferedImage` in memory? I found memory leak may occur if I cache the `BufferedImage` directly, then I translate the image to `byte[]` and cache them, so it seems that it is not a good solution ? – hguser Jul 09 '14 at 05:31
  • @BalusC How to return a default image if image is not found in database (blob is NULL) ? – Amit Sep 25 '14 at 06:54
  • @BalusC That is I am already doing. I am just curious to know any other possible ways.. like we return some error code in response and based on that information img's src is changed.. Thanks any way for your help.. here is the link for question http://stackoverflow.com/questions/26032634/how-to-display-a-default-image-when-image-source-is-a-servlet – Amit Sep 25 '14 at 08:51
  • why did you choose 8192 as size of the array ? – Zied Orabi Feb 25 '22 at 14:32
  • @Zied: same as `BufferedInputStream` and `BufferedOutputStream` themselves are using. See also their source code. – BalusC Feb 25 '22 at 14:43
0

If the images are static, keep in mind that the fastest response is the one that is handled before it gets to you.

You can stand up Apache's httpd in front of your Tomcat server. You can use other variants of caching edge servers. There are plenty of tricks along those lines.

Of course, this supposes that your application is written where a URL effectively maps to one image in a way that is easily cached. If your application lacks this, the benefits are great enough to consider a restructuring.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
0

you can use byte of array type for image from servlet if it is there in Database of Blob type.

byte[] image;

or there is one more way, but it is a bit complex. when u call your servlet, so before that u need to identify if the call is for image or it is normal call. if it is a normal call then u can go ahead to call servlet, but if it a call for image then don't call servlet but u can store the image references at some physical location in computer and retrieve the same.

But this method will not work if u have images in DB, rather u can have relative paths in DB and then u can fetch the image from that path.

M.J.
  • 16,266
  • 28
  • 75
  • 97