Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

The following article describes and shows code for the basics of uploading attachments with Seam, and downloading those attachments.

To start with go ahead and 'seam create-entity' or with JBoss Tools right click on the project and "New -> Seam Entity" and name the Entity "Attachment". We'll only use part of the generated code, mainly only to create the files in the correct locations so you don't need to worry about it.

Next, lets modify the Attachment.java Entity and change it to:

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;

@Entity
public class Attachment implements java.io.Serializable {

	@Id
	@GeneratedValue
	private Long id;
	public Long getId() { return this.id; }
	public void setId(Long id) { this.id = id; }
	
	private String name;
	public String getName() { return this.name;	}
	public void setName(String name) { this.name = name; }
	
	private long size;
	public long getSize() { return this.size; }
	public void setSize(long size) { this.size = size; }
	
	private String contentType;
	public String getContentType() { return this.contentType; }
	public void setContentType(String contentType) { this.contentType = contentType; }
	
	@Lob
	@Column(length = 2147483647)
	@Basic(fetch = FetchType.LAZY)
	private byte[] data;
	public byte[] getData() { return this.data; }
	public void setData(byte[] data) { this.data = data; }
	
}

Now it will contain all of the basic fields to correspond with s:fileUpload.

Open attachment.xhtml and paste in:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:s="http://jboss.com/products/seam/taglib"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml">

	<ui:define name="body">

		<h:messages globalOnly="true" styleClass="message" />

		<h:form enctype="multipart/form-data">

			<rich:panel>
				<f:facet name="header">Upload Attachment</f:facet>

				<s:decorate id="fileUploadDecoration" template="layout/edit.xhtml">
					<ui:define name="label">Attachment</ui:define>
					<s:fileUpload id="file" 
						      data="#{attachmentHome.instance.data}"
						      contentType="#{attachmentHome.instance.contentType}"
						      fileName="#{attachmentHome.instance.name}"
						      fileSize="#{attachmentHome.instance.size}" />
				</s:decorate>
				
				<s:decorate id="nameDecoration" template="layout/display.xhtml">
					<ui:define name="label">Name</ui:define>
					<h:outputText value="#{attachmentHome.instance.name}"/>
				</s:decorate>
				
				<s:decorate id="contentTypeDecoration" template="layout/display.xhtml">
					<ui:define name="label">Content Type</ui:define>
					<h:outputText value="#{attachmentHome.instance.contentType}"/>
				</s:decorate>
				
				<s:decorate id="sizeDecoration" template="layout/display.xhtml">
					<ui:define name="label">Size</ui:define>
					<h:outputText value="#{attachmentHome.instance.size}"/>
				</s:decorate>
				
				<div style="clear: both" />
			</rich:panel>
			
			<div class="actionButtons">
				<h:commandButton value="Upload" 
						 action="#{attachmentHome.persist}"  
						 rendered="#{!attachmentHome.managed}"/>
				<h:commandButton value="Delete" 
						 action="#{attachmentHome.remove}"
						 immediate="true"  
						 rendered="#{attachmentHome.managed}">
					<s:conversationPropagation type="end" />
				</h:commandButton>
				<s:button propagation="end" 
                          id="done" 
                          value="Done"
                          view="/attachmentList.xhtml"/>
			</div>

		</h:form>

	</ui:define>

</ui:composition>

The main difference between the above code and what you would expect with a standard CRUD page is that the fields are outputText instead of inputText because the values are automatically determined by the s:fileUpload component. No code I have here is set in stone, feel free to modify as heavily as you need to, this is just an outline really.

Now, let's edit attachmentList.xhtml to display the properties of the uploaded file and allow for downloading.

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:rich="http://richfaces.org/rich"
                template="layout/template.xhtml">
                       
	<ui:define name="body">
	    
	    <h:messages globalOnly="true" styleClass="message"/>
	    
	    <rich:panel>
	        <f:facet name="header">Files Uploaded</f:facet>
	        
	        <div class="results">
	        
	            <h:outputText value="No files exists" rendered="#{empty attachmentList.resultList}"/>
	                   
	            <rich:dataTable id="attachmentList" 
	            			 var="attachment"
	                      	 value="#{attachmentList.resultList}" 
	                   		 rendered="#{not empty attachmentList.resultList}">
	                <rich:column>
	                    <f:facet name="header">File Name</f:facet>
	                    <s:link value="#{attachment.name}" view="/attachment.xhtml">
	                        <f:param name="attachmentId" value="#{attachment.id}" />
	                    </s:link>
	                </rich:column>
	                <rich:column>
	                    <f:facet name="header">Content Type</f:facet>
	                    #{attachment.contentType}
	                </rich:column>
	                <rich:column>
	                    <f:facet name="header">Size(bytes)</f:facet>
	                    #{attachment.size}
	                </rich:column>
	                <rich:column>
	                    <f:facet name="header">Action</f:facet>
	                    <s:link value="Download" action="#{downloadAttachment.download}">
		                    <f:param name="attachmentId" value="${attachment.id}" />
		                </s:link>
	                </rich:column>
	            </rich:dataTable>
	            
	        </div>
	        
	    </rich:panel>
	    
	    <div class="actionButtons">
	        <s:button id="done" 
	               value="Create attachment"
	                view="/attachment.xhtml"/>			  
	    </div>
	    
	</ui:define>

</ui:composition>

As you can see there is a Component called 'downloadAttachment' referenced that we haven't created yet so lets do that. You can make this an EJB3 or a Seam pojo, the latter is easier so we'll do that. The DownloadAttachment component is the real meat and potatoes of this whole article, so pay attention:

@Name("downloadAttachment")
public class DownloadAttachment {
	
	@Logger
	private Log log;
	
	@In
	private EntityManager entityManager;
	
	@In(value="#{facesContext.externalContext}")
	private ExternalContext extCtx;
	
	@In(value="#{facesContext}")
	FacesContext facesContext;
	
	@RequestParameter
	private Long attachmentId;
	
	public String download() {
		Attachment attachment = entityManager.find(Attachment.class, attachmentId);
		HttpServletResponse response = (HttpServletResponse)extCtx.getResponse();
		response.setContentType(attachment.getContentType());
                response.addHeader("Content-disposition", "attachment; filename=\"" + attachment.getName() +"\"");
		try {
			ServletOutputStream os = response.getOutputStream();
			os.write(attachment.getData());
			os.flush();
			os.close();
			facesContext.responseComplete();
		} catch(Exception e) {
			log.error("\nFailure : " + e.toString() + "\n");
		}

		return null;
	}
}

And for the sake of completeness, here is the AttachmentHome.java and AttachmentList.java which required no changes:

import org.jboss.seam.annotations.Name;
import org.jboss.seam.framework.EntityQuery;

@Name("attachmentList")
public class AttachmentList extends EntityQuery
{
    @Override
    public String getEjbql() 
    { 
        return "select attachment from Attachment attachment";
    }
}
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.framework.EntityHome;

import org.domain.knowledgebase.entity.Attachment;

@Name("attachmentHome")
public class AttachmentHome extends EntityHome<Attachment> {

    @RequestParameter 
    Long attachmentId;
    
    @Override
    public Object getId() { 
        if (attachmentId==null) {
            return super.getId();
        } else {
            return attachmentId;
        }
    }
    
    @Override @Begin
    public void create() {
        super.create();
    }
 	
}

Now the only thing left to do is add exception handling for FileUploadExceptions and define the multipart filter in components.xml.

Add the following to pages.xml:

<exception class="org.jboss.seam.web.FileUploadException">
  <redirect view-id="/error.xhtml">
    <message>#{org.jboss.seam.handledException.message}</message>
  </redirect>
</exception>

And to components.xml:

xmlns:web="http://jboss.com/products/seam/web"
...
http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.0.xsd
...
<web:multipart-filter create-temp-files="true"
                      max-request-size="1000000" 
                      url-pattern="*.seam" />

Incorporating this into your own Webapp

If you want to make this seamless with your your own app you can rip out the form in the attachment.xhtml page and the dataTable in the attachmentList.xhtml page and embed that xhtml in any page you need, it should 'just work'.

9 comments:
 
24. Aug 2008, 14:19 CET | Link

Very useful tutorial, thanks a lot.

ReplyQuote
 
26. Aug 2008, 02:52 CET | Link

Yes, very helpful.

 
03. Oct 2008, 13:21 CET | Link

Sorry, anyone knows how to DISPLAY the image in my app??

 
27. Aug 2008, 09:50 CET | Link
PipoXP (TH) | pipoxp123.AT.gmail.com

Very useful, thanks so much.

 
03. Sep 2008, 04:00 CET | Link

This example only explains how to save files into a database. Is there a way to save files to a location on the filesystem? Is there any reason why saving files inside the database vs the filesystem? Wouldn't that put added load on the DB?

 
05. Sep 2008, 21:53 CET | Link

Excuse me, Thank you for your view, but could help?

What we need is upload a text file A Basa data, and if possible, open it in a front XHTML ...

Serer IS possible to do this?

I tried TU example, but by giving them upload, ME sack of implementation, and does not meet the expected response .....

 
09. Sep 2008, 16:17 CET | Link

Hello all,

In my case, I need to download a file (shows pop-up to user choose betwen download or open) AND redirect to a new page, could be using Seam page navigation, such as: return success. and .page.xml rules.

Does anyone have any suggestion? Because when I add: facesContext.responseComplete();, it can not redirect any more.

 
09. Sep 2008, 19:46 CET | Link
DOWNLOAD

IF YOU NEED TO DOWNLOAD ONE FILE IN THE PRESENTATION, LOOK THIS PAGE:

http://docs.jboss.com/seam/1.2.1.GA/reference/en/html/itext.html

 
14. Nov 2008, 08:51 CET | Link
quangdaocct

Hi all. This is exam about save file in Database. But i want to ask about how i can save file at the folder in the web server. Thanks very much.

Post Comment