Writing image to servlet response with best performance

32,343

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.

Share:
32,343
tabdulin
Author by

tabdulin

Updated on July 09, 2020

Comments

  • tabdulin
    tabdulin almost 4 years

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

  • Gareth Davis
    Gareth Davis almost 14 years
    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.
  • hguser
    hguser almost 10 years
    Hi, while you say "do not use byte[]" you use byte[] buffer = new byte[8192];, then what is the point?
  • BalusC
    BalusC almost 10 years
    @hguser: I clarified the answer.
  • hguser
    hguser almost 10 years
    @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 ?
  • Amit
    Amit over 9 years
    @BalusC How to return a default image if image is not found in database (blob is NULL) ?
  • Amit
    Amit over 9 years
    @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 stackoverflow.com/questions/26032634/…
  • Zied Orabi
    Zied Orabi about 2 years
    why did you choose 8192 as size of the array ?
  • BalusC
    BalusC about 2 years
    @Zied: same as BufferedInputStream and BufferedOutputStream themselves are using. See also their source code.