This blog article is part 2 of a series of posts showing how to deal with images or files in an ADF application. Each of the techniques to do this are described in other blog posts or documents, but I couldn’t find a sample doing it all together in on sample application.
The goal of this series is to provide a sample showing how to upload a file from a client to the server, store the data on the server, make it available for later retrieval and show the data in the user interface (form and table).
Part 1 gives an overview of the sample application I’m going to build and how to set it up
Part 2 shows how to upload a file, store it and download it back to the client
Part 3 implements two techniques to show the data (image) on the user interface
Part 4 backport of the sample to JDeveloper 11gR1
Part 5 implements a technique to show the uploaded file right after upload without the need to commit first
Uploading, downloading and storing of data in a blob
In this part of the series I show how to upload a file from the client to the server and store the data in a blob column in a db. The tables I’m using are CATALOG and IMAGES. The DML to define the tables and can be found in PART 1 of the series or in the sample workspace at the end of this part.
Lets start with uploading a file from client to the server. ADF rich faces provide the tag af:inputFile to allow uploading of data.
<af:inputFile label="Select new file" id="if1" autoSubmit="true"
valueChangeListener="#{ImageBean.uploadFileValueChangeEvent}"/>
As you see the tag has set its autoSubmit property to true to allow direct upload of data. The real work is done in the valueChangeListener which is bound to a backing bean in request scope. The value the event carries allows access to the data and give us the original filename and mime type.
public void uploadFileValueChangeEvent(ValueChangeEvent valueChangeEvent)
{
// The event give access to an Uploade dFile which contains data about the file and its content
UploadedFile file = (UploadedFile) valueChangeEvent.getNewValue();
// Get the original file name
String fileName = file.getFilename();
// get the mime type
String contentType = ContentTypes.get(fileName);
// get the current roew from the ImagesView2Iterator via the binding
DCBindingContainer lBindingContainer =
(DCBindingContainer) BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding lBinding = lBindingContainer.findIteratorBinding("ImagesView2Iterator");
Row newRow = lBinding.getCurrentRow();
// set the file name
newRow.setAttribute("ImageName", fileName);
// create the BlobDomain and set it into the row
newRow.setAttribute("ImageData", createBlobDomain(file));
// set the mime type
newRow.setAttribute("ContentType", contentType);
}
private BlobDomain createBlobDomain(UploadedFile file)
{
// init the internal variables
InputStream in = null;
BlobDomain blobDomain = null;
OutputStream out = null;
try
{
// Get the input stream representing the data from the client
in = file.getInputStream();
// create the BlobDomain datatype to store the data in the db
blobDomain = new BlobDomain();
// get the outputStream for hte BlobDomain
out = blobDomain.getBinaryOutputStream();
// copy the input stream into the output stream
/*
* IOUtils is a class from the Apache Commons IO Package (http://www.apache.org/)
* Here version 2.0.1 is used
* please download it directly from http://projects.apache.org/projects/commons_io.html
*/
IOUtils.copy(in, out);
}
catch (IOException e)
{
e.printStackTrace();
}
catch (SQLException e)
{
e.fillInStackTrace();
}
// return the filled BlobDomain
return blobDomain;
}
Please note the I use the Apache Commons IO package in the version 2.0.1 which you need to download from the Apache Software Foundation web side. The use the class IOUtils which allow easy copying of streams.
If your are using an older version of JDeveloper you may need to add the usesUpload property to the af:form tag. In the current JDev version it should work automatically (but please check it).
<af:form id="f1" usesUpload="true">
If you use fragments (as in this sample) you need to check the jspx or jsf page which is holding the af:form tag (Catalog.jsf), as the fragments don’t have a form tag.
By default, Oracle ADF 11g application allows to upload maximum 2 MB size files. This maximum can be configured in the web.xml file if you need to upload files bigger then 2 MB. For this you need to specify the context parameters
org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY
org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE
org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR
For more information about the parameters and how they work check the doc Oracle® Fusion Middleware Web User Interface Developer’s Guide for Oracle Application Development Framework.
Now to the download part. This is handled in ADF via the af:fileDownloadActionListener tag. The tag is a client listener tag and is therefor applied to a command ui tag. In the sample I use a af:commandButton:
<af:commandButton text="Download Data" id="cb3"
visible="#{bindings.ImageData.inputValue ne null}"
binding="#{ImageBean.downloadButton}">
<af:fileDownloadActionListener contentType="#{bindings.ContentType.inputValue}"
filename="#{bindings.ImageName.inputValue}"
method="#{ImageBean.downloadImage}"/>
</af:commandButton>
The real work is done in the downloadImage method in the managed bean. The signature of the method is
public void downloadImage(FacesContext facesContext, OutputStream outputStream)
This allows you to access to the FacesContext and the output stream which you use to pipe the data to the client.
public void downloadImage(FacesContext facesContext, OutputStream outputStream)
{
BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
// get an ADF attributevalue from the ADF page definitions
AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("ImageData");
if (attr == null)
{
return;
}
// the value is a BlobDomain data type
BlobDomain blob = (BlobDomain) attr.getInputValue();
try
{ // copy hte data from the BlobDomain to the output stream
IOUtils.copy(blob.getInputStream(), outputStream);
// cloase the blob to release the recources
blob.closeInputStream();
// flush the outout stream
outputStream.flush();
}
catch (IOException e)
{
// handle errors
e.printStackTrace();
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), "");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
}
This is all you need to do to download a blob from the db and send it back to the client.
I like to mention one other function of the sample application. If you hit the ‘Cancel’ button in the insert or edit image page I use a rollback to get the original data back and remove all changes made in the form. A rollback resets all current row pointers of the iterators. To avoid that the user sees the first row of the catalog table the rollback has to be handled in a special way. You have to save the current row (of the catalog iterator), do the rollback and reset the current row back to the saved one. This is done in the bean method
public String cancel_action() {...}
which you find in the ImageBean class.
This concludes part 2. In part three I implement two techniques to show the data (image) on the user interface (page)
The sample application workspace is build with JDeveloper 11.1.2.1.0 and can be loaded from here BlogUploadDownload_P2.zip
Please rename the file to ‘.zip’ after downloading it!
The Commons IO package in the version 2.0.1 you can download from the Apache Software Foundation apache web side
To be continued…