Handling Images and other File Data

Using JavaServer Faces

Designing and implementing the various components required or useful for uploading, storing, and accessing images and other file data is a fairly complex tasks with a number of different steps.  This discussion lists of the basic steps and provide sample code to illustrate key technical features.  I will do so within the context of a system that is using javaServer Faces and a MySQL database, developed within an Eclipse IDE.  A useful discussion of this approach is the BalusC Code's Uploading files with JSF.

1. Define the form element for uploading a file. The code shown is for a Faces JSP, using the Tomahawk taglib:  first, the required jsp directive, then the form tag with the required mulltipart enctype parameter, and finally the tomahawk inputFileUpload tag.  Data from these tags may require the Tomahawk 1.2 api.

<%@ taglib prefix="t"  uri="http://myfaces.apache.org/tomahawk" %>
.
.
.
<h:form id="productAdminForm" enctype="multipart/form-data" >
.
.
.
<t:inputFileUpload id="uploadFile" value="#{managedBeanProductAdmin.uploadFile}" />
.
.
.
</h:form>
		

2. Add an ExtensionFilter to the applications web.xml file, along with a mapping to the FacesServlet, to enable processing of multipart form data.

<filter>
	<filter-name>Extensions Filter</filter-name>
	<filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>Extensions Filter</filter-name>
	<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
	

3.  Create a Faces ManagedBean property for the file, along with appropriate getters and setters:

import org.apache.myfaces.custom.fileupload.UploadedFile;
.
.
.
private UploadedFile uploadFile = null;

4.  Create a DBMS table that includes an Integer key, most likely one auto-generated with consecutive values (e.g., 1, 2, 3, . . . ).

5.  Add an item to the DBMS for an entity that includes information about the image, such as its mime type.  The key, if auto-generated should be returned to the controller to be used for generating the path and file name for the image.  Note that the JDBC Statement executeUpdate method must include a parameter that asks the DBMS to make the generated key available.

	int result = statement.executeUpdate( query, Statement.RETURN_GENERATED_KEYS );
	.
	.
	.
	ResultSet resultSet  = statement.getGeneratedKeys();
	if (resultSet.next()) {
	  productId = resultSet.getInt(1);
	} else {
	  throw new SQLException();
	}
	dataBean.setProductId(productId);
		

6.  After storing the information about the image in the DBMS, store the image, itself, in the file system.  The process may be initiated either in the ManagedBean controaller or the model and will take several steps.  In this example, the image path and filename will be a function of the productId returned to the controller as part of a DataBean.

//  write image to file
long imageFileLength = writeImageToFile( uploadFile, dataBeanNew, Constant.IMAGELARGE );				
		

7.  Build the path and file name. The strategy is to build the path for the image file in three parts:  a prefix which is the file system location of the root directory for the images relative to the application context, the hierarchical directory structure and file name for the image, and the suffix which is the mime type for the image.  There are actually two root directories:   one for large images (root) and one for small images (rootSmall).  The paths to those directories are specified in the example by constants:  Constant.IMAGEFILEPREFIX  and Constant.IMAGEFILESMALLPREFIX.

private long writeImageToFile( UploadedFile file, BeanProduct dataBean, int imageSize) {

	if ( file == null )
		return 0;
		
	try {
			
		//  build path
		Integer imageId = dataBean.getProductId();  // productId used as id for image
			
		String prefix = "";
		if ( imageSize == Constant.IMAGELARGE )
			prefix = getImagePrefix( Constant.IMAGEFILEPREFIX ); // /image/root/
		else 
			prefix = getImagePrefix( Constant.IMAGEFILESMALLPREFIX ); // /image/rootSmall/
			
		String suffix = "." + dataBean.getImageType();		
		String path = Util.getImagePath(prefix, imageId, suffix);
		
		//  get InputStream
		
		InputStream inputStream = file.getInputStream();
			
		// write file
		return Util.fileWrite( inputStream, path );
		
	} catch (FileNotFoundException e) {

		e.printStackTrace();
		return 0;
			
	} catch (IOException e) {
			
		e.printStackTrace();
		return 0;
	}

 }
		

8.  For Java to be able to write the file, it must obtain the absolute path to the workspace or context.  for a process running in a JavaServer, this absolute path may be obtained from the ServletContext.  Once it is obtained, the relative path below that point can be generated. 

private String getImagePrefix( String prefixOfPrefix )  {
		
	String prefix = "";
		
	ServletContext servletContext = session.getServletContext();		
	prefix = (servletContext.getRealPath(prefixOfPrefix)) + "/";
		
	return prefix;
}

9.  The core of the path -- the directory hierarchy and file name -- is generated from the DBMS id.  Constant.IMAGEFILEMODULUS is the maximum number of images to be placed in a directory.  The full path to the image file is composed from this core path, along with the prefix and suffix.

	String path;
	int tempInt = imageId;
        String[] names = new String[4];
        
        for (int i=0;i<4;i++)  {
            names[i] = Integer.toString(tempInt % Constant.IMAGEFILEMODULUS);
            tempInt = tempInt / Constant.IMAGEFILEMODULUS;
        }
       
        path  =  names[3] + "/" + names[2] + "/" + names[1] + "/" + names[0];

		

9.  The complete path can be assembled from the (complete) prefix, the core path of the image file (shown above), and the suffix.  Note the clean-up steps to assure proper punctuation of the full path, below.

	public static String getImagePath( String prefix, Integer imageId, String suffix ) {
	
		//	note:  depending on prefix, can be used for either 
		//	path for java write or url for server access.

		//  assures prefix ends in / or \
		//  assures suffix  begins with .
		
		//  assure prefix ends with separator
		String assuredPrefix = prefix;
		if ( ! (prefix.endsWith("/") || prefix.endsWith("\\")) )
			assuredPrefix = prefix + "/";

		//  assures suffix  begins with .
		String assuredSuffix = suffix;
		if ( ! ((suffix.substring(0,1)).equals(".")) )
			assuredSuffix = "." + suffix;

		
		String url;        
        
        	url  = assuredPrefix;
       	 	url += mapImageIdToDirPath( imageId );  //  see 8, above
        	url += assuredSuffix;
        
        	return url;
        
	}	

10.  Actually write the image to the file:

	public static long fileWrite(InputStream inputStream, String filePathAndName) {
		
	  File outputFile;
		
	  try {
	    int lastSlash = filePathAndName.lastIndexOf("/");
	    String filePath = filePathAndName.substring(0, lastSlash);
	    File directories = new File( filePath );
	    directories.mkdirs();
        
	   try {
				
	      outputFile = new File ( filePathAndName );
	      FileOutputStream outputStream = new FileOutputStream( outputFile );
	      int bytesRead = 0;
	      byte[] buffer = new byte[8192];
				
              while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
              }
              outputStream.close();
              inputStream.close();
            		
	    } catch (FileNotFoundException e) {
	      System.out.println("fileWrite failed: FileNotFoundException");
	      return -1;      
	    } catch (IOException e) {
	      System.out.println("fileWrite failed: IOException");
	      return -1;        
	    }
        
	    long size = outputFile.length();
	    return size;
        
	  }  catch ( Exception e )  {	    
	     System.out.println("fileWrite failed: Exception");
	     return -1;        
	  }
    
	}