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.
| Online: | 6 Members of 12451 |
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'.
Very useful tutorial, thanks a lot.
Yes, very helpful.
Very useful, thanks so much.
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?
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 .....
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 . and .page.xml rules.
Does anyone have any suggestion? Because when I add: facesContext.responseComplete();, it can not redirect any more.
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
Sorry, anyone knows how to DISPLAY the image in my app??
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.
Hello,
The download does not get the right file from the database. Looks like it saves the current xhtml to the file when we try to download.
Anyone with the same Problem?
Thanks
For some reason I don't know, I'm getting null values for data, filename, contentType ....
Any tip?
I have JBoss 4.2.2, Seam 2.1, IceFaces 1.7.2 SP1.
I can't use IceFaces fileInput, because it doesn't work well with FireFox 3 and Chrome.
Thanks.
Hello
I also have the error, that im only getting null values from the s:fileupload
I use Jboss 5.0.1 and jboss 2.1.1
if somebody have a tip please contact me!!!
thx kapi
AFAIK s:upload calls twice. One time with null values and one time with the actual file / upload data. If you test for the null and just return then, it should work.
Do you solve this problem? I have the same
Hello Rafael,
Yes, the problem was solved. 1st issue, the attribute names used in this example are invalid, for example for a oracle database, so, use ur own attribute names. That is basic. And you will have to update in all files, in the example.
2nd, Check if you have a warning while declaring the entity manager in the DownloadAttachment component, this causes the issue you are facing. So get it solved.
3rd, Make sure you are giving the right parameter to the download link on attachmentList.xhtml
<s:link value="Download" action="#{downloadAttachment.download}"> <f:param name="AttachmentIdFile" value="#{_attachment.idFile}" /> </s:link>If you have any trouble, just contact... Hope it helps
I had a problem when i try to download.
Exception during request processing:
...........
...........
...........
...........
Please help me, how to solve the problem. thanks.
This works for me up until I want to edit other parts of the record, without changing the image.
How do I avoid uploading blank data to the Datbase (postgres / orcale) when I want to update everything but the image?
Many thanks Andrew
@kapi
I was getting this same problem (null values on upload), see my forum post for my solution:
SeamFilter called twice, breaks s:fileUpload
Is it possible to redirect to different pages. I tried using xml for navigation. But it doesnt work out.
Does anyone have a solution?
Thanks in advance.
Work like a charm. A big thank you!
and made some code to download a file from special folder.
Here are my codes:
1. Download interface class
2. Download action class
3. Download xhtml file
Here aie the action class:
public String download() {
String fileName = downloadFilename;
log.info("downloade(),uploadFile.getName()="+fileName);
if (fileName == null ) {
fileName = "test.txt";
}
String contentType = getContentType(fileName);
log.info("downloade(),contentType="+contentType);
try {
HttpServletResponse response = (HttpServletResponse)extCtx.getResponse();
log.info("downloade(),1 response="+response);
log.info("downloade(),before etResponse.");
if ( facesContext == null ) {
facesContext = new FacesContext();
}
response = (HttpServletResponse)facesContext.getContext().getExternalContext().getResponse();
log.info("downloade(),2 response="+response);
response.reset();
response.setContentType(contentType);
response.addHeader("Content-disposition", "attachment; filename=\"" + fileName +"\"");
//ServletOutputStream os = response.getOutputStream();
OutputStream os = response.getOutputStream();
File file = new File( fileLoc, fileName);
log.info("downloade(),file="+file);
byte[] fileBytes = FileUtil.readBytes( file );
os.write(fileBytes);
os.flush();
os.close();
//facesContext.responseComplete();
} catch(Exception ex) {
log.error("downloade(),Failure : " + ex.toString());
}
return null;
}
I got this error messages:
java.lang.IllegalStateException: Servlet response already use stream, Writer not possible
at org.ajax4jsf.webapp.FilterServletResponseWrapper.getWriter(FilterServletResponseWrapper.java:207)
at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)
..
..
Do you know what is my prblem?
Thanks
Chang Shin
Great article! It worked in the first try, thanks guys!
I got the above error when I mistakenly omitted the facesContext.responseComplete() call. The error (for me) was actually on the originating view (that contains the link to download the file) upon redisplay. I didn't discover this snafu until I added some logging and noticed that my file streaming was actually completing successfully.
Double check your h:form if you really did set enctype=.
That's a common mistake why you might be missing the data.
Otherwise tips for others, don't forget to add the required imports to file DownloadAttachment as below,
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.log.Log;
Also don't forget to create a table called attachment to store the attachments, the structure of the table i used is
id int(4) NOT NULL AUTO_INCREMENT - Primary Key
name varchar(50),
size int(4),
contentType varchar(50),
data blob,
Also on uploading you might get an error to do with file size being exceeded, try uploading a smaller file.
I think for larger files use the LongBlob datatype
Hope it helps
Hi,
Thanks for this wonderful article. By forcing facesContext.responseComplete(), we get a browser download dialog. however, any re-rendering that must be done after download is complete can't be done here.
Do you have any suggestion on how re-rendering of certain component can be triggered after download?
Thanks, Shriharsh
useful tutorial
I am also getting the same issue i.e. the download does not get the right file from the database but it saves the current xhtml to the file when we try to download.
Here is what I am tried...
xxxxList.xhtml
<rich:column>
<f:facet name="header">Action</f:facet>
<s:link value="Download" action="#{downloadAttachment.download}">
<f:param name="metTestAttachmentHjid" value="${metTestAttachment.hjid}" />
</s:link>
</rich:column>
@Name("downloadAttachment")
public class DownloadAttachment {
@In
private EntityManager entityManager;
@In(value="#{facesContext.externalContext}")
private ExternalContext extCtx;
@In(value="#{facesContext}")
FacesContext facesContext;
@RequestParameter
private Long metTestAttachmentHjid;
public String download() {
MetTestAttachment attachment = entityManager.find(MetTestAttachment.class, metTestAttachmentHjid);
HttpServletResponse response = (HttpServletResponse)extCtx.getResponse();
//THIS RETURNS CORRECT ID OF THE FILE
log.info("DownloadAttachment.download, attachment.getContenttype() check id :"+metTestAttachmentHjid);
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");
}
}
}
Entity:
public class MetTestAttachment implements java.io.Serializable {
private Long hjid;
private String name;
private String contenttype;
private byte[] data;
private int filesize;
....
}
Home object
@Name("metTestAttachmentHome")
public class MetTestAttachmentHome extends EntityHome<MetTestAttachment> {
@RequestParameter
Long metTestAttachmentHjid;
@RequestParameter
Long id;
public void setMetTestAttachmentHjid(Long id) {
setId(id);
}
public Long getMetTestAttachmentHjid() {
return (Long) getId();
}
}
Any suggestion for best approach to upload multiple files..? I will try rich:fileUpload ... to check
Just an FYI in case anyone is struggling with this, the code as is was working for me, then when we threw in a special kind of attachment, it began throwing the exception for me, I re-arranged it so that the code generating the byte array is called before the servlet response is acquired, and the problem did not recur.
So instead of using:
Because attachment.getData() in my case was taking a long time, I did something like this instead:
byte[] data = attachment.getData(); // This takes some time HttpServletResponse response = (HttpServletResponse) extCtx.getResponse(); response.setContentType(format); response.addHeader("Content-disposition", "attachment; filename=\"" + attachment.getName() +"\""); ServletOutputStream os = response.getOutputStream(); os.write(data); os.flush(); os.close(); facesContext.responseComplete();I am not certain why this is, or if it is a race condition that I am now simply beating. An approach to avoid storing it in a byte and use streams array might be to kick off the long running part of the attachment, and wait to acquire the output stream until the byte stream is ready.
There are some very great sources here and thank you for being so kind to post them here. So we can read them and give our opinion on pariuri live subject.