Fancy File Upload Widget

179 views
Skip to first unread message

Adam T

unread,
Aug 15, 2006, 5:59:01 PM8/15/06
to Google Web Toolkit
Ages ago I had a go at a fancy file upload widget that tried to emulate
GMail's way with a pending period and some styles for various states,
but it got messy and I gave up
(http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/13e1bfd3f7be66f3/20a1f6305459dc89?lnk=gst&q=File+Upload+Widget&rnum=13#20a1f6305459dc89)

Now though, with the new form panels and form event handling, I had
another go, and it now works at least on the browsers I have tried.
You can click on Browse to select a file, which then goes into a
pending state, then after a determined delay (where the user can stop
the upload if they wish) the file is uploaded. After upload, if the
user clicks on the checkbox, the file is attempted to be deleted from
the server.

You can attach a change listener to te widget to listen for changes
when a file has been uploaded or deleted (and then perhaps check it
status), e.g.

final FancyFileUpload testFile = new FancyFileUpload();
testFile.addChangeListener(new ChangeListener(){
public void onChange(Widget sender) {
Window.alert("File status "+testFile.getUploadState());
}
});

(it proved silly to have a onChange event fired for all state changes
as they happen so quick)

The next two posts will contain the client and server code that anyone
is most welcome to use (at their own risk, e.g. the server side has
relatively no security on it, but I am sure you could fix that if you
wished!!).

//Adam

Adam T

unread,
Aug 15, 2006, 6:00:29 PM8/15/06
to Google Web Toolkit
Client side Java Code:


import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ChangeListenerCollection;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormHandler;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormSubmitEvent;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasWordWrap;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;


public class FancyFileUpload extends Composite implements HasText,
HasWordWrap, SourcesChangeEvents{


/**
* State definitions
*/
public final int EMPTY_STATE = 1;
public final int PENDING_STATE = 2;
public final int UPLOADING_STATE = 3;
public final int UPLOADED_STATE = 4;
public final int DELETED_STATE = 5;
public final int FAILED_STATE = 6;

/**
* Initial State of the widget.
*/
private int widgetState = EMPTY_STATE;

/**
* Default delay to check an empty FileUpload widget for
* arrival of a filename.
*
*/
private int searchUpdateDelay = 500;

/**
* Default delay for pending state, when delay over the form is
submitted.
*/
private int pendingUpdateDelay = 5000;

/**
* OK message expected from file upload servlet to indicate successful
upload.
*/
private String returnOKMessage = "OK";

private FormPanel uploadForm = new FormPanel();
private VerticalPanel mainPanel = new VerticalPanel();

/**
* Internal timer for checking fileupload text for a value.
*/
private Timer t;

/**
* Internal timer for checking if pending delay is over.
*/
private Timer p;

/**
* Widget representing file to be uploaded.
*/
private UploadDisplay uploadItem;

/**
* FileName to be uploaded
*/
String fileName = "";

/**
* Class used for the display of filename to be uploaded,
* and handling the update of the display states.
* @author tacyad
*
*/protected class UploadDisplay extends Composite{

/**
* FileUpload Widget
*/
FileUpload uploadFileWidget = new FileUpload();

/**
* Label to display after file widget is filled with a filename
*/
Label uploadFileName = new Label();


/**
* Checkbox to invoke deletion functionality of the file.
*/
CheckBox proceed = new CheckBox();

/**
* Panel to hold the widget
*/
FlowPanel mainPanel = new FlowPanel();

/**
* Panel to hold pending, loading, loaded or failed state details.
*/
HorizontalPanel pendingPanel = new HorizontalPanel();

/**
* Constructor
*
*/
public UploadDisplay(){
mainPanel.add(uploadFileWidget);

pendingPanel.add(uploadFileName);
uploadFileName.setWordWrap(true);
pendingPanel.add(proceed);
proceed.setChecked(true);
// Set up a click listener on the proceed check box
proceed.addClickListener(new ClickListener(){
public void onClick(Widget sender) {
// If clicked we need to check the status of the upload.
if(widgetState==UPLOADED_STATE){
// File has previously been uploaded, so now must be deleted.
// set the encoding type of the form to be urlencoded (alters
functionality of the servlet)
uploadForm.setEncoding(FormPanel.ENCODING_URLENCODED);
// Set the status.
setDeleted();
// Call the servlet
deleteFiles();
} else {
// If file has not been uploaded, just reset the widget
reset();
// and then start waiting again.
t.cancel();
p.cancel();
startWaiting();
}
}
});
mainPanel.add(pendingPanel);
pendingPanel.setVisible(false);
initWidget(mainPanel);
}

/**
* Set the widget into pending mode by altering style
* of pending panel and displaying it. Hide the FileUpload
* widget and finally set the state to Pending.
*
*/
private void setPending(){
uploadFileName.setText(uploadFileWidget.getFilename());
uploadFileWidget.setVisible(false);
pendingPanel.setVisible(true);
pendingPanel.setStyleName("fancyfileupload-pending");
widgetState = PENDING_STATE;
}

/**
* Set the widget into Loading mode by changing the style name
* and updating the widget State to Uploading.
*
*/
private void setLoading(){
pendingPanel.setStyleName("fancyfileupload-loading");
widgetState = UPLOADING_STATE;
}

/**
* Set the widget to Loaded mode by changing the style name
* and updating the widget State to Loaded.
*
*/
private void setLoaded(){
pendingPanel.setStyleName("fancyfileupload-loaded");
widgetState = UPLOADED_STATE;
}

/**
* Set the widget to Deleted mode by changing the style name
* and updating the widget State to Deleted.
*
*/
private void setDeleted(){
pendingPanel.setStyleName("fancyfileupload-deleted");
widgetState = DELETED_STATE;
}

/**
* Set the widget to Failed mode by changing the style name
* and updating the widget State to Failed.
* Additionally, hide the pending panel and display the FileUpload
* widget.
*
*/
private void setFailed(){
widgetState = FAILED_STATE;
uploadFileName.setText("Operation Failed");
}

/**
* Reset the display
*
*/
private void reset(){
fileName = uploadFileName.getText();
widgetState = EMPTY_STATE;
uploadFileName.setText("");
uploadFileWidget.setVisible(true);
pendingPanel.setVisible(false);
proceed.setChecked(true);
}
}


/**
* Perform the uploading of a file by changing state of display widget
* and then calling form.submit() method.
*
*/
private void uploadFiles(){
fileName = uploadItem.uploadFileWidget.getFilename();
if (uploadItem.proceed.isChecked()){
uploadItem.setLoading();
uploadForm.submit();
}
}


private void deleteFiles(){
uploadForm.submit();
}

/**
* Put the widget into a Pending state, set the Pending delay timer
* to call the upload file method when ran out.
*
*/
private void pendingUpload(){
// Fire an onChange event to anyone who is listening
uploadItem.setPending();
p = new Timer(){
public void run() {
uploadFiles();
}
};
p.schedule(pendingUpdateDelay);
}

/**
* Method to check if FileUpload Widget has a filename within it.
* If so, cancel the timer that was set to call this method and then
* call the pendingUpload() method.
* If not, do nothing.
*
*/
private void checkForFileName(){
GWT.log(uploadItem.uploadFileWidget.getFilename()+" :
"+fileName,null);
if (!uploadItem.uploadFileWidget.getFilename().equals("")){
if (!uploadItem.uploadFileWidget.getFilename().equals(fileName)){
t.cancel();
pendingUpload();
}
}
}

/**
* This method sets up a repeating schedule to call the
checkforfilename
* method to see if the FileUpload widget has any text in it.
*
*/
private void startWaiting(){
t = null;
t = new Timer(){
public void run() {
checkForFileName();
}
};
t.scheduleRepeating(searchUpdateDelay);
}


/**
* Constructor.
*
*/
public FancyFileUpload(){
// Set Form details
// Set the action to call on submit
uploadForm.setAction("/upload");
// Set the form encoding to multipart to indicate a file upload
uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
// Set the method to Post
uploadForm.setMethod(FormPanel.METHOD_POST);
uploadForm.setWidget(mainPanel);

// Create a new upload display widget
uploadItem = new UploadDisplay();
// Set the name of the upload file form element
uploadItem.uploadFileWidget.setName("uploadFormElement");
// Add the new widget to the panel.
mainPanel.add(uploadItem);

// Start the waiting for a name to appear in the file upload
widget.
startWaiting();
// Initialise the widget.
initWidget(uploadForm);

// Add an event handler to the form.
uploadForm.addFormHandler(new FormHandler() {
public void onSubmitComplete(FormSubmitCompleteEvent event) {
// Fire an onChange Event
fireChangeEvent();
// Cancel all timers to be absolutely sure nothing is going on.
t.cancel();
p.cancel();
// Ensure that the form encoding is set correctly.
uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
// Check the result to see if an OK message is returned from
the server.
if(event.getResults().toString().equals(returnOKMessage)){
// If yes, and this is an upload then set the widget to loaded
state.
if (!(widgetState==DELETED_STATE)) {
uploadItem.setLoaded();
} else {
// Do nothing
}
} else {
// If no, set the widget to failed state.
uploadItem.setFailed();
}
}

public void onSubmit(FormSubmitEvent event) {
//No validation in this version.
}
});
}

/**
* Fire a change event to anyone listening to us.
*
*/
private void fireChangeEvent(){
if (changeListeners != null)
changeListeners.fireChange(this);
}

/**
* Get the text from the widget - which in reality will be retrieving
any
* value set in the Label element of the display widget.
*/
public String getText() {
return uploadItem.uploadFileName.getText();
}

/**
* Cannot set the text of a File Upload Widget, so raise an exception.
*/
public void setText(String text) {
throw new RuntimeException("Cannot set text of a FileUpload Widget");
}

/**
* Retrieve the status of the upload widget.
* @return Status of upload widget.
*/
public int getUploadState(){
return widgetState;
}

/**
* Set the delay for checking for a filename to appear in the
FileUpload widget
* Might be useful if there are performance issues.
* @param newDelay
*/
public void setCheckForFileNameDelay(int newDelay){
searchUpdateDelay = newDelay;
}

/**
* Set the delay value indicating how long a file will remain in
pending mode
* prior to the upload action taking place.
* @param newDelay
*/
public void setPendingDelay(int newDelay){
pendingUpdateDelay = newDelay;
}

/**
* Return the delay value set for checking a file.
* @return
*/
public int getCheckForFileNameDelay(){
return searchUpdateDelay;
}

/**
* Return value set for pending delay.
* @return
*/
public int getPendingDelay(){
return pendingUpdateDelay;
}

/**
* Return if the label in the display widget is wordwrapped or not.
*/
public boolean getWordWrap() {
return uploadItem.uploadFileName.getWordWrap();
}

/**
* Set the word wrap value of the label in the display widget.
*/
public void setWordWrap(boolean wrap) {
uploadItem.uploadFileName.setWordWrap(wrap);
}

private ChangeListenerCollection changeListeners;

/**
* Add a change listener
* @param listener
*/
public void addChangeListener(ChangeListener listener) {
if (changeListeners == null)
changeListeners = new ChangeListenerCollection();
changeListeners.add(listener);
}

/**
* Remove a change listener
* @param listener
*/
public void removeChangeListener(ChangeListener listener) {
if (changeListeners != null)
changeListeners.remove(listener);
}
}


Example CSS:

.fancyfileupload-pending{
font-family: arial;
font-size: 10px;
background: orange;
width: 200px;
}

.fancyfileupload-loading{
font-family: arial;
font-size: 10px;
background: red;
width: 200px;
}


.fancyfileupload-loaded{
font-family: arial;
font-size: 10px;
background: lightgreen;
width: 200px;
}

.fancyfileupload-failed{
font-family: arial;
font-size: 10px;
background: blue;
width: 200px;
}

Adam T

unread,
Aug 15, 2006, 6:00:59 PM8/15/06
to Google Web Toolkit
Client side code:

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;

public class FileUploadServlet extends HttpServlet{
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

String rootDirectory = "C:/aaUploadTemp/";
boolean writeToFile = true;
String returnOKMessage = "OK";

boolean isMultipart = ServletFileUpload.isMultipartContent(request);

PrintWriter out = response.getWriter();

// Create a factory for disk-based file items

if (isMultipart) {
// We are uploading a file (deletes are performed by on multipart
requests)
FileItemFactory factory = new DiskFileItemFactory();

// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// Parse the request
try {
List items = upload.parseRequest(request);
// Process the uploaded items
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();

if (item.isFormField()) {
} else {
if (writeToFile) {
String fileName = item.getName();
if (fileName != null && !fileName.equals("")) {
fileName = FilenameUtils.getName(fileName);
File uploadedFile = new File(rootDirectory+fileName);
try {
item.write(uploadedFile);
out.print(returnOKMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
} else {
//Process a request to delete a file
String[] paramValues =
request.getParameterValues("uploadFormElement");
for (int i=0;i<paramValues.length;i++){
String fileName = FilenameUtils.getName(paramValues[i]);
File deleteFile = new File(rootDirectory+fileName);
if(deleteFile.delete()){
out.print(returnOKMessage);
}
}
}
}
}

Reply all
Reply to author
Forward
0 new messages