Serve your images
Update: there's a newer article out for more effective file serving. You may find it useful: FileServlet supporting resume and caching and GZIP.
If you have stored images in a path outside of the web container or in a database, then the client cannot access the images directly by a relative URI. A good practice is to create a Servlet which loads the image from a path outside of the web container or from a database and then streams the image to the HttpServletResponse. You can pass the file name or the file ID as a part of the request URI. You can also consider to pass it as a request parameter, but that would cause problems with getting the filename right when the user want to save the image in certain web browsers by rightclicking and saving it (Internet Explorer and so on).
Back to top
ImageServlet serving from absolute path
Here is a basic example of a ImageServlet which serves a image from a path outside of the web container.
package mypackage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URLDecoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * The Image servlet for serving from absolute path. * @author BalusC * @link http://balusc.blogspot.com/2007/04/imageservlet.html */ public class ImageServlet extends HttpServlet { // Constants ---------------------------------------------------------------------------------- private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB. // Properties --------------------------------------------------------------------------------- private String imagePath; // Actions ------------------------------------------------------------------------------------ public void init() throws ServletException { // Define base path somehow. You can define it as init-param of the servlet. this.imagePath = "/images"; // In a Windows environment with the Applicationserver running on the // c: volume, the above path is exactly the same as "c:\images". // In UNIX, it is just straightforward "/images". // If you have stored files in the WebContent of a WAR, for example in the // "/WEB-INF/images" folder, then you can retrieve the absolute path by: // this.imagePath = getServletContext().getRealPath("/WEB-INF/images"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Get requested image by path info. String requestedImage = request.getPathInfo(); // Check if file name is actually supplied to the request URI. if (requestedImage == null) { // Do your thing if the image is not supplied to the request URI. // Throw an exception, or send 404, or show default/warning image, or just ignore it. response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. return; } // Decode the file name (might contain spaces and on) and prepare file object. File image = new File(imagePath, URLDecoder.decode(requestedImage, "UTF-8")); // Check if file actually exists in filesystem. if (!image.exists()) { // Do your thing if the file appears to be non-existing. // Throw an exception, or send 404, or show default/warning image, or just ignore it. response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. return; } // Get content type by filename. String contentType = getServletContext().getMimeType(image.getName()); // Check if file is actually an image (avoid download of other files by hackers!). // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp if (contentType == null || !contentType.startsWith("image")) { // Do your thing if the file appears not being a real image. // Throw an exception, or send 404, or show default/warning image, or just ignore it. response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. return; } // Init servlet response. response.reset(); response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setHeader("Content-Type", contentType); response.setHeader("Content-Length", String.valueOf(image.length())); response.setHeader("Content-Disposition", "inline; filename=\"" + image.getName() + "\""); // Prepare streams. BufferedInputStream input = null; BufferedOutputStream output = null; try { // Open streams. input = new BufferedInputStream(new FileInputStream(image), DEFAULT_BUFFER_SIZE); output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE); // Write file contents to response. byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int length; while ((length = input.read(buffer)) > 0) { output.write(buffer, 0, length); } // Finalize task. output.flush(); } finally { // Gently close streams. close(output); close(input); } } // Helpers (can be refactored to public utility class) ---------------------------------------- private static void close(Closeable resource) { if (resource != null) { try { resource.close(); } catch (IOException e) { // Do your thing with the exception. Print it, log it or mail it. e.printStackTrace(); } } } }
In order to get the ImageServlet to work, add the following entries to the Web Deployment Descriptor web.xml:
<servlet> <servlet-name>imageServlet</servlet-name> <servlet-class>mypackage.ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>imageServlet</servlet-name> <url-pattern>/image/*</url-pattern> </servlet-mapping>
Of course you can change the url-pattern of the servlet-mapping as you like it.
Here are some basic use examples:
<!-- XHTML or JSP --> <img src="image/test.jpg" /> <img src="image/test.gif" /> <!-- JSF --> <h:graphicImage value="image/test.jpg" /> <h:graphicImage value="image/test.gif" /> <h:graphicImage value="image/#{myBean.imageFileName}" />
Back to top
ImageServlet serving from database
First prepare a DTO (Data Transfer Object) for Image which can be used to hold information about the image. You can map this DTO to the database and use a DAO class to obtain it. You can get the image as InputStream from the database using ResultSet#getBinaryStream().
The data layer and the DAO pattern is explained in this tutorial: DAO tutorial - the data layer.
package mymodel; import java.io.InputStream; public class Image { // Init --------------------------------------------------------------------------------------- private String id; private String name; private String contentType; private InputStream content; private Integer length; // Implement default getters and setters here. }
Here is a basic example of a ImageServlet which serves a image from a database.
package mypackage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import mydao.DaoFactory; import mydao.ImageDAO; import mymodel.Image; /** * The Image servlet for serving from database. * @author BalusC * @link http://balusc.blogspot.com/2007/04/imageservlet.html */ public class ImageServlet extends HttpServlet { // Constants ---------------------------------------------------------------------------------- private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB. // Statics ------------------------------------------------------------------------------------ private static ImageDAO imageDAO = DAOFactory.getImageDAO(); // Actions ------------------------------------------------------------------------------------ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Get ID from request. String imageId = request.getParameter("id"); // Check if ID is supplied to the request. if (imageId == null) { // Do your thing if the ID is not supplied to the request. // Throw an exception, or send 404, or show default/warning image, or just ignore it. response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. return; } // Lookup Image by ImageId in database. // Do your "SELECT * FROM Image WHERE ImageID" thing. Image image = imageDAO.find(imageId); // Check if image is actually retrieved from database. if (image == null) { // Do your thing if the image does not exist in database. // Throw an exception, or send 404, or show default/warning image, or just ignore it. response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404. return; } // Init servlet response. response.reset(); response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setHeader("Content-Type", image.getContentType()); response.setHeader("Content-Length", String.valueOf(image.getLength())); response.setHeader("Content-Disposition", "inline; filename=\"" + image.getName() + "\""); // Prepare streams. BufferedInputStream input = null; BufferedOutputStream output = null; try { // Open streams. input = new BufferedInputStream(image.getContent(), DEFAULT_BUFFER_SIZE); output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE); // Write file contents to response. byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int length; while ((length = input.read(buffer)) > 0) { output.write(buffer, 0, length); } // Finalize task. output.flush(); } finally { // Gently close streams. close(output); close(input); } } // Helpers (can be refactored to public utility class) ---------------------------------------- private static void close(Closeable resource) { if (resource != null) { try { resource.close(); } catch (IOException e) { // Do your thing with the exception. Print it, log it or mail it. e.printStackTrace(); } } } }
In order to get the ImageServlet to work, add the following entries to the Web Deployment Descriptor web.xml:
<servlet> <servlet-name>imageServlet</servlet-name> <servlet-class>mypackage.ImageServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>imageServlet</servlet-name> <url-pattern>/image/*</url-pattern> </servlet-mapping>
Of course you can change the url-pattern of the servlet-mapping as you like it.
Here are some basic use examples:
<!-- XHTML or JSP --> <img src="image?id=250d7f5086d02a46f9aeec9c51d43c63" /> <img src="image?id=0412c29576c708cf0155e8de242169b1" /> <!-- JSF --> <h:graphicImage value="image?id=250d7f5086d02a46f9aeec9c51d43c63" /> <h:graphicImage value="image?id=0412c29576c708cf0155e8de242169b1" /> <h:graphicImage value="image?id=#{myBean.imageId}" />
Back to top
Security considerations
In the last example of an ImageServlet serving from database, the ID is encrypted by MD5. It's your choice how you want to implement the use of ID, but keep in mind that plain numeric ID's like 1, 2, 3 and so on makes the hacker easy to guess for another images in the database, which they probably may not view at all. Then rather use a MD5 hash based on a combination of the numeric ID, the filename and the filesize for example. And last but not least, use PreparedStatement instead of a basic Statement to request the image by ID from database, otherwise you will risk an SQL injection when a hacker calls for example "image?id=';TRUNCATE TABLE Image--".
Back to top
Copyright - There is no copyright on the code. You can copy, change and distribute it freely. Just mentioning this site should be fair.
(C) April 2007, BalusC

22 comments:
very nice example
You are the greatest. Thanx for your code and be sure I will go on singing your name. However I want to ask; do you know the code for displaying, say, 50 rows from a resultset in multiple pages, say 10 rows per page as in website search engines. Thanx.
It is really much beneficial. Major thing is that it is upto the point. Thank you so much.
Its really a nice code. Well structured and creatively generated. I think you must be such a great programmer. Especially at this part of code:
response.reset(); response.setContentLength(contentLength); response.setContentType(image.getContentType()); response.setHeader( "Content-disposition", "inline; filename=\"" + image.getFileName() + "\""); output = new BufferedOutputStream(response.getOutputStream());
BUT THE PROBLEM IS... Where is your import mydao.*;????!!!
This is a fictive DAO. Just use your own DAO thing :)
WOO, so cool. It is the greatest image retrieve from database example I saw.
Was very useful and really helped me in a situation when i was completely stuck...!
Good example.
Give me briefly idean how to do.
but i still have a question, in my case, Im not getting an image from a folder and post in web browser.I got a code that need to be converted into an image and display on the web browser in jsp page.Thank you
@Amanda Lau: check the Java 2D API.
I´m having a problem with JSF object Image in Firefox 3 and the Image Servlet. Page doesn´t load, and to get loaded i have to hit the "Refresh" button of the browser severall times. the same code works on firefox 2.x and worked in Firefox 3 but, since i´ve upgraded the glassfish server and Netbeans, whenever i use the Image Servlet the page doesn´t get loaded properly ... any ideas?
Hi BalusC
First of all I want to thank you for making my life easier!
Now to my question, i am trying to display a image wich i am recovering from my mysql-db. I am converting this image from blob to byte[] in my bean. I want to display this image in my jsp page. Is there a way of doing this without using servlets?
i read somewere that h:graphicImage value="#{MyBean.imageArray}" should do the work, but i end up with
java.lang.ClassCastException
can i make this work or is the onley way to go with a servlet?
thank you for your time!
You cannot put the raw byte array of the image as a value inside the 'src' attribute. The 'src' attribute must point to an URL where the image is located. Thus, yes, a Servlet is the only right way.
Yes i figured that out =) i used your example and it works perfect!
thank you!
Thanks so much for this example . It worked out but my senior wants me to try without a servlet. Here's whay i did. I called a method in my backing bean on click of a button which does exactly what ur servlet does. Problem : My image comes in a seperate jspx whose url only differs some sessionid at the end.
Now how can i get my image to display in a page i specify that too within an img tag (or its like)Any clue??
is there any way I can pass the inputText value to the servlet parameter
graphicImage width="75"
value="/imageServlet?partyRefNo=#{DisplayBean.partyRef)"
After you have submitted the form, yes. The EL in your example is only invalid. It shouldn't close with a parentheses ), but with a curly brace }.
Thanks, very usefull article. I have used your idea to get images from static content server in my JSF application.
I agree - this really is a very useful example. But how about caching? I have an application where the user will upload an image to a database. This image will then be displayed on the next page a for all future pages they see on that visit. Will it be necessary to go back to the database for each page?
Thanks,
Tom
Worked it out for myself - just added the line:
response.setHeader("Expires", "Thu, 15 Apr 2010 20:00:00 GMT");
to the servlet (where the date is any date in the future obviously) and caching occurred.
Thanks again.
Tom
I got the code's working.It is taking image from local file system and encoding it into the URL.
But how should I call this servlet .If my file is located at c:\zero.jpg
Just give simple example.
Thanks :)
That's already explained in the article. Do not only copypaste the ImageServlet class, but also read the article's text and the remnant code snippets for web.xml and JSP file.
Good code , it worked for me :)
Post a Comment