【问题标题】:GMail like file upload progress bar with GWT?GMail 之类的文件上传进度条与 GWT?
【发布时间】:2010-10-12 02:42:32
【问题描述】:

所有Gmail用户应该已经注意到文件上传进度条最近更新了。

我想知道这样的效果是否可以用 GWT 来实现。 我是 GWT 的新手,所以如果有任何 GWT 源代码可以帮助我测试该功能会非常有帮助。

更新
我最终选择了 SWFUpload。但是,这个问题下的其他建议都是有效的。只需尝试不同的选项,然后选择您喜欢的选项!

【问题讨论】:

    标签: gwt


    【解决方案1】:

    有自定义多上传插件演示http://ext4all.com/post/extjs-4-multiple-file-upload

    【讨论】:

      【解决方案2】:

      我以前用过这个工具:

      http://code.google.com/p/gwt-fileapi/

      与这里的其他建议不同,它不仅提供了适当的 API 来显示上传进度,还提供了通过选择多个文件进行批量上传的能力,还提供了拖放支持。它还具有预 HTML5 后备机制。

      gwt-fileap 让我很幸运。最近它在 Firefox 7 和 8 中出现故障,我不得不对它应用这个补丁 - 但除此之外它真的很好用:

      @@ -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
      

      我还必须将以下几行添加到http://code.google.com/p/gwt-fileapi/source/browse/trunk/gwt-fileapi/src/com/gwtpro/html5/fileapi/Html5FileApi.gwt.xml - 这些行描述了回退机制的工作原理。如果您希望您的代码在缺少 HTML5 的情况下使用下面显示的 SWFUploader 实现,您可以执行类似的操作。

          <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>
      

      这是我在我的应用程序中使用它的方式:

      这是描述抽象的接口:

      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();
      
      }
      

      以下是接口的gwt-fileapi实现:

      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/>");
          }
      
      }
      

      这是同一接口的SWFUpload http://code.google.com/p/swfupload-gwt/实现:

      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();
      
          }
      }
      

      这是 FileUploadSwfImpl 所依赖的实用程序:

      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;
          }
      }
      

      【讨论】:

        【解决方案3】:

        最近我开始了自己的一个项目,名为gwtupld

        http://github.com/kompot/gwtupld/

        主要目标是为尖端浏览器提供最佳文件上传体验 和所有其他人可接受的可用性。目前,存在以下主要功能

        • 多文件选择
        • 拖放
        • 进度条
        • 简洁的外观
        • 所有浏览器的一致行为
        • 易于视觉定制
        • 没有外部依赖,只有 GWT

        随意分叉和提交错误/功能建议。 您可以查看源代码,然后输入

        gradlew gwtcompile devmode
        

        并得到它将开始一个功能齐全的 沙箱(具有真实文件保存的服务器端应该可以工作)

        【讨论】:

          【解决方案4】:

          看看这个库:http://code.google.com/p/gwtupload/。它非常易于使用,并且在我检查过的所有浏览器和操作系统中都能正常工作。它使用 ajax 请求来计算进度。顺便说一句,Swfupload 在 linux 和 Mac 中表现不佳。

          【讨论】:

          【解决方案5】:

          创建您自己的文件上传进度时,您可以让客户端显示一个不确定的条 2 秒,然后让服务器计算预计完成时间,然后将更改返回确定而是每 5、10 秒提取一次新的估计值。这应该对流量几乎没有影响。

          【讨论】:

            【解决方案6】:

            您可以使用 GwtSwfExt,它是 SWFUpload 之上的包装器(与 Swfupload-gwt lib 相同),您可以从 http://code.google.com/p/gwtswfext 下载示例和源代码。

            【讨论】:

              【解决方案7】:

              通过swfupload-gwt使用SWFUpload

              与其他方法相比的主要优点是它不需要任何特殊的服务器代码。您甚至可以上传到另一个域(如果有允许它的 crossdomain.xml)。

              【讨论】:

              • 另外...(希望)这是一个短期的解决方案,我希望 Gears 提供的功能能够在浏览器中本地使用。 code.google.com/apis/gears/…
              【解决方案8】:

              如果您有一个 java 后端,编写自己的代码很简单,您只需启动文件上传,然后在计时器上轮询服务器以查看它的进度(比如每隔一两秒)。 java 文件上传二进制文件(apache commons 二进制文件)支持告诉你当前的进度,所以这很简单。

              【讨论】:

              • 嗯,我认为它可以更优雅,因为每秒轮询服务器太麻烦了,尤其是一直有网络瓶颈的 Web 应用程序。 (也许我没有从你想说的话中看出?)
              • 一种非轮询技术是使用 Flash 上传器。这就是 getdropbox.com 的上传方式——它看起来很棒,我相信你可以并行上传文件。在 www.getdropbox.com 上查看
              • @Chii,这就是我想出来的。我认为 Gmail 也使用 flash 上传器,因为它们嵌入了 swf 文件。你知道如何在 Flash 中做到这一点并与典型的 Web 应用程序集成吗? (我已经知道 Dropbox :D)
              • 每秒轮询并发送少于 1kb 的数据非常小。除非您有 10,000 个用户,否则我看不出问题吗?
              • 无轮询的问题是你必须实现一些“彗星式”的解决方案。它是:服务器必须在准备好时提醒客户端,这意味着客户端必须保持一个打开的连接,这意味着经典的 servlet api 不能很好地扩展(我认为 servlet3.0 包含了这个差距的解决方案)
              【解决方案9】:

              查看GWTC Upload,它有一个完全符合您要求的实现。

              【讨论】:

              • 我没有看到任何有用的东西。通常,有源代码可供下载或查看。
              • 单击源选项卡:# 非成员可以通过 HTTP 匿名签出只读工作副本。 svn checkout gwtcupload.googlecode.com/svn/trunkgwtcupload-read-only
              猜你喜欢
              • 2012-09-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多