View Source

h1. Incremental Upload Processing Using FileEntryCallback

The all-new ACE FileEntry component introduces in-memory file processing. This feature is designed to provide for antivirus scanning, checksum verification or a scenario where the upload doesn't _need_ to be saved to disk, and doing so immediately is excessive IO.

The functionality requires that a listener bean, implementing FileEntryCallback, handle the incrementally uploaded bytes. The methods that instances of FileEntryCallback must implement are:

/* Notification for upload handler when a file begins uploading.
* fileInfo - information known about the file, before downloading the contents */
public void begin(FileEntryResults.FileInfo fileInfo);

/* We're working with chunks of bytes, as we receive them... */
public void write(byte[] buffer, int offset, int length);
public void write(int data);

/* Notification for upload handler that the file is finished.
* Should we decide to invalidate an upload, perhaps it's over quota, then this method
* must handle the case. This method may massage the result (raising some
* prompts and accepting upload), or possibly fail the upload for good by calling
* FileInfo.updateStatus(FileEntryStatus newStatus, boolean success) on the FileInfo input.
* fileInfo - The same object that was passed into begin(FileInfo)
public void end(FileEntryResults.FileInfo fileInfo);

This example demos in-memory processing to provide an MD5 hash for an uploaded file.

* [*Implementing FileEntryCallback*|#interface]
* [*Adding the FileEntry Component to Facelet Page*|#addFileFacelet]
* [*Adding the "Quota Exceeded" Dialog*|#addQuotaFacelet]
h2. {anchor:interface}Implementing FileEntryCallback
* Check fileInfo.getStatus() to determine if the file has pre-failed uploading, due to too many files uploaded, an invalid file extension, or content type.
Creating our handler is the most important step of this tutorial. Everything else is just wiring up the dependant parts.

* h3. The Class Properties


* h3. The {{begin}} Method
// Set up a instance of a MD5 block-encoder
public void begin(FileEntryResults.FileInfo fileInfo) {
try { digest = MessageDigest.getInstance("MD5"); }
catch (NoSuchAlgorithmException e) {
FacesUtils.addErrorMessage("MD5 Algorithm Not Available.");

* h3. The {{write}} Methods
// Hash a block of bytes
public void write(byte[] bytes, int offset, int length) {
digest.update(bytes, offset, length);
// Hash a single byte
public void write(int i) { digest.update((byte) i); }

* h3. The {{end}} Method
// When FileEntryCallback ends for a file:
public void end(FileEntryResults.FileInfo fileEntryInfo) {
// If the file upload was completed properly
if (fileEntryInfo.getStatus().isSuccess()) {
// Implement some application-layer checks on the entire file if needed
// etc.
// If those went fine add a success message.
FacesUtils.addInfoMessage(TutorialMessageUtils.getMessage("content.callback.result.message") + " " + getHash());
} else {
// Implement error handling given for specific status cases,
// or you could use your own general fail message.
FacesContext c = FacesContext.getCurrentInstance();
UIComponent comp = c.getViewRoot().findComponent("fileEntry");
clientId = comp.getClientId();
c.addMessage(null, fileEntryInfo.getStatus().getFacesMessage(c, comp, fileEntryInfo));

h2. {anchor:addFileFacelet}Adding the FileEntry Component to a Facelet Page

That concludes this overview of the new in-memory processing feature. If you're interested in further details of how this example works, take a look at the complete source code below; or sign up for ICEfaces training for a complete guide to this example and every feature of ICEfaces!