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());
}
}