Tutorial :GMail like file upload progress bar with GWT?


All Gmail users should have already noticed that file upload progress bar has been updated recently.

I'm wondering such effect is possible to implement with GWT. I'm fairly new with GWT, so if any GWT source code that can help me test out the function would be very helpful.

I ended up going with SWFUpload. However, other suggestions under this question are all valid. Just try different options and choose the one you like!


Take a look to this library: http://code.google.com/p/gwtupload/. It is really easy to to use and works fine in all browsers and OS I've checked. It uses ajax requests to calculate progress. BTW Swfupload doesn't do well in linux and Mac.


I've used this tool before:


Unlike the other suggestions here, not only does it give the proper API to show upload progress, it also gives the ability to do batch uploads by selecting multiple files, and it also gives drag and drop support. It also has a pre HTML5 fallback mechanism.

I've had had great luck with it gwt-fileap. Recently it broke in Firefox 7 and 8 and I had to apply this patch to it - but otherwise it works really great:

@@ -57,26 +57,33 @@         /**        * gets the filename  -     *   +     *        * @return the filename        */       public final native String getFileName() /*-{  -        return this.fileName;  +        if(this.name)  +                    return this.name;  +         else  +                    return this.fileName;  +       }-*/;         /**        * gets the file size in bytes  -     *   +     *        * @return the file size in bytes        */       public final native int getFileSize() /*-{  -        return this.fileSize;  +        if(this.size)  +                    return this.size;  +         else  +                    return this.fileSize;       }-*/;         /**        * gets the MIME type of the file, may be null if the browser cannot detect        * the type  

I also had to add the following lines to http://code.google.com/p/gwt-fileapi/source/browse/trunk/gwt-fileapi/src/com/gwtpro/html5/fileapi/Html5FileApi.gwt.xml - these lines describe how the fallback mechanism works. You can do something similar if you want your code to fall back on the SWFUploader implementation shown below in case HTML5 is missing.

    <define-property name="fileapi.support" values="yes,no" />        <property-provider name="fileapi.support"><![CDATA[                     var input=document.createElement('input');                      input.setAttribute('type','file');                      return input.files==null?'no':'yes';      ]]></property-provider>          <replace-with              class="com.gwtpro.html5.fileapi.client.ui.FileInput.FileInputImplHtml5">              <when-type-is                      class="com.gwtpro.html5.fileapi.client.ui.FileInput.FileInputImpl" />              <when-property-is name="fileapi.support" value="yes" />              <any>                      <when-property-is name="user.agent" value="ie8" />                      <when-property-is name="user.agent" value="safari" />                      <when-property-is name="user.agent" value="gecko1_8" />                      <when-property-is name="user.agent" value="opera" />                      <when-property-is name="user.agent" value="chrome" />               </any>      </replace-with>  

This is how I use it in my application:

This is the interface that describes the abstraction:

public interface FileUpload {      public void uploadFiles();      public Widget getWidget();      public void initialize(Grid updateTable, Uploader uploader, String url, boolean createDropHandler);      public void setDisabled(boolean b);      public  void readyToPaint();      public void reset();    }  

The following is the gwt-fileapi implementation of the interface:

package com.hierarchycm.gxt.client.fileUpload;    import com.google.gwt.core.client.JsArray;  import com.google.gwt.event.dom.client.ChangeEvent;  import com.google.gwt.event.dom.client.ChangeHandler;  import com.google.gwt.http.client.RequestException;  import com.google.gwt.http.client.Response;  import com.google.gwt.user.client.Window;  import com.google.gwt.user.client.ui.Grid;  import com.google.gwt.user.client.ui.RootPanel;  import com.google.gwt.user.client.ui.Widget;  import com.gwtpro.html5.fileapi.client.FileApiSupport;  import com.gwtpro.html5.fileapi.client.drop.DropHandler;  import com.gwtpro.html5.fileapi.client.file.File;  import com.gwtpro.html5.fileapi.client.file.FileEvent;  import com.gwtpro.html5.fileapi.client.file.FileEvent.FileEventHandler;  import com.gwtpro.html5.fileapi.client.ui.FileInput;  import com.gwtpro.html5.fileapi.client.upload.UploadRequest;  import com.gwtpro.html5.fileapi.client.upload.UploadRequestBuilder;  import com.gwtpro.html5.fileapi.client.upload.UploadRequestCallback;    public class FileUploadHtmlImpl extends FileInput implements FileUpload {        private Grid uploadTable;         int currentFile =0;      String url;      File[] files;         UploadRequestBuilder fileUploader;      Uploader uploader;        public FileUploadHtmlImpl() {        }        FileUploadHtmlImpl(Grid updateTable, Uploader uploader, String url) {          this(updateTable, uploader, url, true);      }        FileUploadHtmlImpl(Grid updateTable, Uploader uploader, String url, boolean createDropHandler) {          initialize(updateTable, uploader, url, createDropHandler);          //this.setCallback(getMyCallback());      }        public void initialize(Grid updateTable, Uploader uploader, String url, boolean createDropHandler){          this.url = url;          this.uploadTable = updateTable;          this.uploader = uploader;          this.setAllowMultipleFiles(true);          this.addChangeHandler(new ChangeHandler() {                  @Override                  public void onChange(ChangeEvent event) {                      addFiles(FileUploadHtmlImpl.this.getFiles());                         uploadFiles();                  }            });            if (createDropHandler) {              createDropHandler();          }      }         private File[] jsArrToArr (JsArray<File> ipFiles) {              File [] result = new File [ipFiles.length()];                  for (int i = 0; i < ipFiles.length(); ++i) {               result[i] = ipFiles.get(i);           }           return result;       }        private UploadRequestCallback getMyCallback() {          return new UploadRequestCallback() {                @Override              public void onError(UploadRequest request, Throwable exception) {                  uploadTable.setText(currentFile + 1, 2, "failed: " + exception.getMessage());                  uploadNextFile(currentFile + 1);              }                @Override              public void onResponseReceived(UploadRequest request, Response response) {                  uploadTable.setText(currentFile + 1, 2, "success: " + response.getText());                  uploadNextFile(currentFile + 1);                    //If we just finished uploading  do your thing                  if (currentFile == files.length) {                      setDisabled(false);                      uploader.uploadDoneEventHandler();                  }              }                @Override              public void onUploadProgress(UploadRequest request, int bytesUploaded) {                  uploadTable.setText(currentFile + 1, 2, bytesUploaded + "");              }          };      }        public void createDropHandler() {            RootPanel rootPanel = RootPanel.get();            DropHandler dropHandler = new DropHandler(rootPanel);              this.fileUploader = new UploadRequestBuilder(url);              this.fileUploader.setCallback(getMyCallback());              dropHandler.addFileEventHandler(new FileEventHandler() {                    @Override                  public void onFiles(FileEvent event) {                      addFiles(jsArrToArr(event.getFiles()));                      uploadFiles();                  }              });      }         private void addFiles (File[] ipFiles) {              files = ipFiles;              uploadTable.clear();              uploadTable.resize(files.length + 1, 3);              uploadTable.setText(0, 0, "File name");              uploadTable.setText(0, 1, "File size");              uploadTable.setText(0, 2, "Progress");              for (int i = 0; i < files.length; ++i) {                                  uploadTable.setText(i + 1, 0, files[i].getFileName());                                            uploadTable.setText(i + 1, 1, files[i].getFileSize() + "");                  uploadTable.setText(i + 1, 2, "");              }      }        public void uploadNextFile(int index) {              for (String paramName : uploader.getPostParams().keySet()) {                  fileUploader.setHeader(paramName, uploader.getPostParams().get(paramName));                                                   }                currentFile = index;              this.setDisabled(true);              if (index < this.files.length) {                  try {                      this.fileUploader.setHeader("itemName", files[currentFile].getFileName());                      this.fileUploader.sendFile(files[currentFile]);                  } catch (RequestException e) {                      this.uploadTable.setText(index + 1, 2, "failed: " + e.getMessage());                      uploadNextFile(index + 1);                  }              }           }        public void uploadFiles() {          uploadNextFile(0);      }        @Override      public Widget getWidget() {          return this;      }        @Override      public void readyToPaint() {          //no need to do anything - already painted for non swf      }        @Override      public void reset() {          // TODO Auto-generated method stub        }        private void showCapabilities() {          RootPanel                  .get("status")                  .getElement()                  .setInnerHTML(                          "Drag and Drop Support: "                                  + (FileApiSupport.isDragDropSupported() ? "Yes"                                          : "No")                                  + "<br/>HTTPXmlRequest Level 2: "                                  + (FileApiSupport.isHttpXmlRequestLevel2() ? "Yes"                                          : "No")                                  + "<br/>File input supports multiple files: "                                  + (FileApiSupport                                          .isMultipleFileInputSupported() ? "Yes"                                          : "No")+"<br/><br/>");      }    }  

This is the SWFUpload http://code.google.com/p/swfupload-gwt/ implementation of the same interface:

package com.hierarchycm.gxt.client.fileUpload;    import com.extjs.gxt.ui.client.widget.Html;  import com.google.gwt.user.client.ui.Grid;  import com.google.gwt.user.client.ui.Widget;    public class FileUploadSwfImpl extends Html implements FileUpload {        SwfUploadUtil swfUploadUtil = null;      private Uploader uploader;      private String url;      private boolean createDropHandler;      private Grid updateTable;            static int uploadId = 0;       static String divTagId;        public FileUploadSwfImpl() {          divTagId = "swfupload" + uploadId++;          String divTag = "<div id=\"" + divTagId + "\"></div";          this.setHtml(divTag);      }        @Override      public void uploadFiles() {          swfUploadUtil.startUpload();              }        @Override      public Widget getWidget() {               return this;      }         public  void readyToPaint() {                   swfUploadUtil =  new SwfUploadUtil(uploader, updateTable, divTagId, url);              }        @Override      public void initialize(Grid updateTable, Uploader uploader, String url, boolean createDropHandler) {            this.uploader = uploader;          this.url = url;          this.createDropHandler = createDropHandler;          this.updateTable = updateTable;        }        @Override      public void setDisabled(boolean b) {            swfUploadUtil.setDisabled(b);          this.disabled = true;        }        @Override      public void reset() {          swfUploadUtil.reset();        }  }  

And this is the utility the FileUploadSwfImpl depends on:

package com.hierarchycm.gxt.client.fileUpload;    import java.util.HashMap;    import org.swfupload.client.File;  import org.swfupload.client.SWFUpload;  import org.swfupload.client.UploadBuilder;  import org.swfupload.client.SWFUpload.ButtonAction;  import org.swfupload.client.SWFUpload.ButtonCursor;  import org.swfupload.client.event.DialogStartHandler;  import org.swfupload.client.event.FileDialogCompleteHandler;  import org.swfupload.client.event.FileQueuedHandler;  import org.swfupload.client.event.UploadCompleteHandler;  import org.swfupload.client.event.UploadErrorHandler;  import org.swfupload.client.event.UploadProgressHandler;  import org.swfupload.client.event.UploadSuccessHandler;  import org.swfupload.client.event.FileDialogCompleteHandler.FileDialogCompleteEvent;  import org.swfupload.client.event.FileQueuedHandler.FileQueuedEvent;  import org.swfupload.client.event.UploadCompleteHandler.UploadCompleteEvent;  import org.swfupload.client.event.UploadErrorHandler.UploadErrorEvent;  import org.swfupload.client.event.UploadProgressHandler.UploadProgressEvent;  import org.swfupload.client.event.UploadSuccessHandler.UploadSuccessEvent;    import com.extjs.gxt.ui.client.widget.form.TextArea;  import com.google.gwt.core.client.GWT;  import com.google.gwt.user.client.Window;  import com.google.gwt.user.client.ui.Grid;    public class SwfUploadUtil {        HashMap<String, Integer> filenameRowHm = new HashMap<String, Integer>();         private boolean resetIssued;        SWFUpload swfUpload = null;      private HashMap <String, File> files = new HashMap<String, File>();           int tableRow = 5;         Uploader uploader = null;      private Grid updateTable;      private String divName;      private String url;        synchronized private void removeFile(String id) {          files.remove(id);      }               public SwfUploadUtil(Uploader uploader, Grid updateTable,  String divName, String url){          reset();          this.uploader = uploader;          this.updateTable = updateTable;          this.divName = divName;          this.url = url;            this.swfUpload = loadSWFUpload();          updateTable.resize(5, 5);          updateTable.setText(2, 0, "Upload URL:" );          updateTable.setText(2, 1, url );                  updateTable.setText(4, 0, "File Name" );          updateTable.setText(4, 1, "Bytes In");          updateTable.setText(4, 2, "Status");          updateTable.setText(4, 3, "File Size" );          updateTable.setText(4, 4, "Server response" );        }          public SWFUpload loadSWFUpload() {            this.updateTable = updateTable;            if (swfUpload == null) {                      final UploadBuilder builder1 = new UploadBuilder();              builder1.setHTTPSuccessCodes(200, 201);              builder1.setFileTypes("*.webm;*.asf;*.wma;*.wmv;*.avi;*.flv;*.swf;*.mpg;*.mpeg;*.mp4;*.mov;*.m4v;*.aac;*.mp3;*.wav;*.png;*.jpg;*.jpeg;*.gif");              builder1.setFileTypesDescription("Images, Video & Sound");                builder1.setButtonPlaceholderID(divName);              builder1.setButtonImageURL("./images/XPButtonUploadText_61x22.png");              builder1.setButtonCursor(ButtonCursor.HAND);              builder1.setButtonWidth(61);              builder1.setButtonHeight(22);              builder1.setButtonAction(ButtonAction.SELECT_FILES);                builder1.setUploadProgressHandler(new UploadProgressHandler() {                    public void onUploadProgress(UploadProgressEvent e) {                        File f = e.getFile();                                         updateTable.setText(getFilenameRow(f), 2, String.valueOf(e.getBytesComplete()));                    }              });                builder1.setUploadSuccessHandler(new UploadSuccessHandler() {                  public void onUploadSuccess(UploadSuccessEvent e) {                      File f = e.getFile();                      updateTable.setText(getFilenameRow(f), 4, e.getServerData());                  }              });                 builder1.setUploadErrorHandler(new UploadErrorHandler() {                  public void onUploadError(UploadErrorEvent e) {                      File ff = e.getFile();                       String message = e.getMessage();                       if (message == null || message.trim().length() == 0) {                          message = "upload failed";                       }                                     updateTable.setText(getFilenameRow(ff), 2, String.valueOf(message));                        removeFile(ff.getId());                       if (files.values().size() > 0) {                          ff = files.values().iterator().next();                           updateTable.setText(getFilenameRow(ff), 2, "Started");                          swfUpload.startUpload(ff.getId());                                            }                  }              });                 builder1.setUploadURL(url);                 builder1.setDialogStartHandler(new DialogStartHandler() {                  @Override                  public void onDialogStart() {                      if(resetIssued == true) {                           filenameRowHm.clear();                          resetIssued = false;                      }                                 }                                 }              );                builder1.setUploadCompleteHandler(new UploadCompleteHandler() {                  public void onUploadComplete(UploadCompleteEvent e) {                      File f = e.getFile();                         updateTable.setText(getFilenameRow(f), 2, "Done");                        removeFile(f.getId());                       if (files.values().size() > 0) {                          File ff = files.values().iterator().next();                             updateTable.setText(getFilenameRow(ff), 2, "Started");                          swfUpload.startUpload(ff.getId());                       } else {                                              uploader.uploadDoneEventHandler();                      }                  }              });                builder1.setFileQueuedHandler(new FileQueuedHandler() {                  public void onFileQueued(FileQueuedEvent event) {                        File f = event.getFile();                                         updateTable.setText(getFilenameRow(f), 2, "Queued");                                          files.put(f.getId(), f);                   }              });                builder1.setFileDialogCompleteHandler(new FileDialogCompleteHandler() {                  public void onFileDialogComplete(FileDialogCompleteEvent e) {                                                                               updateTable.setText(2, 0, "Number of files");                      updateTable.setText(2, 1, String.valueOf(files.values().size()));                        for(File f : files.values()) {                          getFilenameRow(f);                      }                        if (files.values().size() > 0) {                            for (String paramName : uploader.getPostParams().keySet()) {                              swfUpload.addPostParam(paramName,uploader.getPostParams().get(paramName));                                                    }                         }                  }              });              swfUpload = builder1.build();            }            return swfUpload;        }        public int getFilenameRow (File f) {          Integer filenamerow = filenameRowHm.get(f.getId());            if (filenamerow == null) {              updateTable.resize(tableRow+1, 5);              filenamerow = new Integer(tableRow++);              updateTable.setText(filenamerow.intValue(), 0, f.getName());              updateTable.setText(filenamerow.intValue(), 3, String.valueOf(f.getSize()));              //updateTable.setText(filenamerow.intValue(), 3, String.valueOf(f));              filenameRowHm.put(f.getId(), filenamerow);          }            return filenamerow.intValue();      }        public void startUpload() {          uploader.uploadStartedEventHandler();          swfUpload.startUpload();      }        public void setDisabled(boolean disabled) {          swfUpload.setButtonDisabled(disabled);          }        public void reset() {          // TODO Auto-generated method stub          resetIssued = true;      }  }  


Use SWFUpload via swfupload-gwt

The main advantage over the other methods is this does not require any special server code. You could even upload to another domain (if there is a crossdomain.xml which allows it).


Check out GWTC Upload, which has an implementation of exactly what you're looking for.


It's trivial to write your own if you have a java back end, you just start a file upload and then poll the server on a timer to see where it's up to (say every second or two). The java file upload binaries (the apache commons ones) support telling you the current progress so it's trivial to do.


Recently I started a project of my own called gwtupld


The main goal is to provide best file upload experience for cutting edge browsers and acceptable usability for all others. By the moment, following key features are present

  • multiple file selection
  • drag'n'drop
  • progress bars
  • slick and simple exterior
  • consistent behavior for all browsers
  • ease of visual customization
  • no external dependencies but GWT

Feel free to fork and submit bugs/feature proposals. You can check out source code, then type

gradlew gwtcompile devmode  

and get it will start a fully functional sandbox (server side with real file saving should work)


You can use GwtSwfExt which is wrapper on top of SWFUpload (Its same as Swfupload-gwt lib ) you can download example and source code from http://code.google.com/p/gwtswfext.


When creating your own file upload progress, instead of pulling it form server at a small set time, you can have the client to display a indeterminate bar for 2 seconds and have the server calculate the estimated finish time the change back to determinate and pull new estimates every 5, 10 seconds instead. that should have little to no effect on the traffic.


There is custom multiupload plugin demo http://ext4all.com/post/extjs-4-multiple-file-upload

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Next Post »