Managing Images

Created: 11.07.2014

Managing Image Files in Web Applications

There are several ways to organize images within a web application.

If the images are very small, you might consider directly storing them in a database. However, every time a client requests an image (which might happen very often), a new database query will be issued. This stresses the database and might affect other parts of your application.

Another way is store the images in a directory inside the file system of the machine your container runs on. As a side note, it is not recommended to store a dynamically growing number of images directly in your web application. Since the web applicaton is packaged into a war file, this file would grow and grow and also affect the web containers performance. A better way is to define a directory in the file system like /var/your_web_application/images.

To exemplify this procedure, I assume that you want to manage user images. Every registered user is associated with a single image stored in the filesystem.

Step 1: Setup a Directory and a Symbolic Link

The choice of the directory is the most crucial part. If your webserver runs on Linux and the web container has the corresponding access rights, you could setup a directory somewhere in /var/.

A more elegant way is to setup a directory within your web container’s web application folder. Furthermore, to make the web application aware of the existence of such a folder, you need to setup a property in the web containers configuration. It is important that this entry points to the absolute location of your folder (in my test scenario I’m on a local windows machine). This allows the components of your application to access this folder via a url shorthand.

I use Glassfish v4 for my example application, so i modify my glassfish-web.xml file:

<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" 
"http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
    <context-root>/User_Images</context-root>
    <property 
    	name="alternatedocroot_1" 
		value="from=/userimages/* dir= E:/Java/Runtimes/glassfish-4.0/glassfish4/glassfish/domains/domain1/applications/" />
</glassfish-web-app>

There are some important hints to consider:

  • In case of glassfish, it is important to use the property name “alternatedocroot_n” as this is a well-known term and cannot be recognized otherwise.
  • There must be a folder equally named to the one you specified inside the value attribute.

The second point needs some further explanation. Let's have a look at the following example.

If you set:

value="from=/userimages/*

than there has to be the directory:

"E:/Java/Runtimes/glassfish-4.0/glassfish4/glassfish/domains/domain1/applications/userimages/"

The symbolic link would not work, if you pick another name like:

"E:/Java/Runtimes/glassfish-4.0/glassfish4/glassfish/domains/domain1/applications/something_different/"

In my opinion this is somewhat counterintuitiveā€¦

Step 2: Use the Symbolic Link to Reference Images

You can now use urls of the form "images/imagename.jpg" to reference images. Let's look at my sample application which contains a User and an Image class.

public class User {
	private int userId;
	private Image userImage = new Image();
	//omitted set/get
}

Instead of setting just a string that represents the relative path, I created an Image class and added it as an attribute to the user entity. The Image class contains the relative path and additional information, i.e. a link to a thumbnail version.

public class Image {
	public static final String DEFAULT_IMAGE = "/userimages/default_image.png";

	private int imageId;
	private String relativePath = DEFAULT_IMAGE;
	//omitted set/get
}

My handler/controller creates two sample users to display them along with their images in a Facelets view file.

@ManagedBean
@RequestScoped
public class UserHandler {

	private User user1;
	private User user2;

	public UserHandler() {
		user1 = new User();
		user1.setUserId(1);

		user2 = new User();
		user2.setUserId(2);
		Image image = new Image();
		image.setRelativePath("/userimages/user_1.png");
		user2.setUserImage(image);
	}
}

The view file simply requests these two user objects:

<h:body>
	<h:panelGrid columns="2">
		<h:outputText value="#{userHandler.user1.userId}" />
		<h:graphicImage value="#{userHandler.user1.userImage.relativePath}" />
		
		<h:outputText value="#{userHandler.user2.userId}" />
		<h:graphicImage value="#{userHandler.user2.userImage.relativePath}" />
	</h:panelGrid>
</h:body>

There you go! Now you are set to display images easily.

Optional: Create a WebServlet or a Filter to Secure the Access to the File Directory

In the current state of the application, it would be possible to request images via the url:

http://locahost:8080/yourapplication/userimages/some_image.jpg

If you want to protect the images or include checks of maleformed URLs, you need to add a @WebServlet or a Filter that checks all incoming requests to the folder "/userimages/*".

An appropriate WebServlet skeleton could look like this:

@WebServlet("/userimages/*")
public class UserImageProvider extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// get path behind /images/ but skip the leading "/"
		String pathToImage = req.getPathInfo().substring(1);

		// using java7 nio makes life easier
		Path imageFile = Paths.get(pathToImage);

		// setup response
		resp.setHeader("Conent-Type", getServletContext().getMimeType(pathToImage));
		resp.setHeader("Content-Length", String.valueOf(Files.size(imageFile)));

		Files.copy(imageFile, resp.getOutputStream());
	}
}