File download support

Posted by    |       Seam

Web applications often need to provide a link for the user to download a file, such as an expense report. Seam 2.1.2.CR1 makes the task of serving a file to the user easy and RESTful using a combination of the s:resource and s:download tags. But before we can use these tags, we need to do some simple configuration.

The s:resource tag uses Seam’s DocumentStore to serve (or push) documents. You'll need to configure this servlet in to your web.xml:

web.xml

<servlet>
   <servlet-name>Document Store Servlet</servlet-name>
   <servlet-class>org.jboss.seam.document.DocumentStoreServlet</servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>Document Store Servlet</servlet-name>
   <url-pattern>/seam/docstore/*</url-pattern>
</servlet-mapping>

Alone in a page, s:resource will act as the file download provider by defining where to get the file data and meta-data. You use it in place of HTML (not inside of HTML) since the intent is to serve the document and not a web page. Thus, the page template should look something like this.

resources.xhtml

<s:resource xmlns="http://www.w3.org/1999/xhtml"
	xmlns:s="http://jboss.com/products/seam/taglib"
	data="#{fileResourceProvider.data}"
	contentType="#{fileResourceProvider.contentType}"
	fileName="#{fileResourceProvider.fileName}" />
  • data is the actual file data. It may be a java.util.File, an InputStream or a byte[].
  • contentType (e.g., image/jpeg)
  • filename (e.g., someFile.jpg) which will likely be displayed in the user's file download prompt

Other attributes are described in the Seam reference documentation.

If we want resources.xml to serve all of our documents (or at least those handled by the fileResourceProvider component), we need to pass one (or many) parameters to it such that it can load the document information and data for a given request. We do that by using request parameters (e.g., http://localhost/app/resources.seam?fileId=1). There are at least two ways of getting the parameter holding the file id into the fileResourceProvider component. You could use the @RequestParameter annotation;

@RequestParameter
private Long fileId;

or inject it via pages.xml:

<page view-id=”/resources.xhtml”>
   <param name="fileId" value="#{fileResourceProvider.fileId}"/>
</page>

Taking the first approach, the FileResourceProvider.java would then be defined as follows:

@Name("fileResourceProvider")
@Scope(ScopeType.EVENT)
public class FileResourceProvider
{

   @RequestParameter
   private Long fileId;
   private String fileName;
   private String contentType;
   private byte[] data;

   @Create
   public void create()
   {
      FileItem item = em.find(FileItem.class, fileId); // Get document representation
      this.fileName = item.getFileName();
      this.data = item.getData();
      this.contentType = item.getContentType();
   }
   //.. getters setters
}

That's all that is needed to serve documents in a RESTful way. To make the creation of the download link even easier, you can use the s:download tag. Here's how it works:

<s:download src=”/resources.xhtml”>
    <f:param name=”fileId” value=”#{someBean.downloadableFileId}”/>
</s:download>

The s:download tag is a variation on the Seam s:link tag. It will generate a bookmarkable link looking something like http://localhost/resources.seam?fileId=1. You can then use Seam URL rewriting to translate the link into http://localhost/resources/1. See examples in the examples/ui in the Seam distribution for further guidance!


Back to top