Подтвердить что ты не робот

GMail как индикатор загрузки файлов с GWT?

Все пользователи Gmail уже должны были заметить, что обновленный индикатор выполнения загрузки файлов был недавно обновлен.

Мне интересно, какой эффект можно реализовать с помощью GWT. Я новичок в GWT, поэтому, если какой-либо исходный код GWT, который может помочь мне проверить функцию, будет очень полезен.

Обновление
Я закончил с SWFUpload. Тем не менее, все другие предложения по этому вопросу действительны. Просто попробуйте разные варианты и выберите тот, который вам нравится!

4b9b3361

Ответ 1

Взгляните на эту библиотеку: http://code.google.com/p/gwtupload/. Это действительно легко использовать и прекрасно работает во всех браузерах и ОС, которые я проверил. Он использует запросы ajax для расчета прогресса. BTW Swfupload не очень хорошо работает в Linux и Mac.

Ответ 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 - эти строки описывают, как работает резервный механизм. Вы можете сделать что-то подобное, если вы хотите, чтобы ваш код возвращался к реализации SWFUploader, показанной ниже, в случае отсутствия HTML5.

    <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

Использовать SWFUpload через swfupload-gwt

Основным преимуществом над другими методами является отсутствие специального кода сервера. Вы даже можете загрузить в другой домен (если есть crossdomain.xml, который разрешает его).

Ответ 4

Отъезд GWTC Upload, в котором реализована именно то, что вы ищете.

Ответ 5

Тривиально писать свои собственные, если у вас есть задний конец java, вы просто запустите загрузку файла, а затем опросите сервер по таймеру, чтобы увидеть, где он находится (скажем, каждую секунду или две). Файлы java файлов для загрузки файлов (apache commons ones) поддерживают рассказы о текущем прогрессе, поэтому это тривиально.

Ответ 6

Недавно я начал свой проект под названием gwtupld

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

Основная цель - обеспечить наилучшую загрузку файлов для передовых браузеров и приемлемое удобство для всех остальных. К настоящему моменту присутствуют следующие ключевые функции.

  • выбор нескольких файлов
  • drag'n'drop
  • индикаторы выполнения
  • гладкий и простой внешний вид
  • совместимое поведение для всех браузеров
  • легкость визуальной настройки
  • нет внешних зависимостей, но GWT

Не стесняйтесь вилки и отправляйте ошибки/предложения функций. Вы можете проверить исходный код, затем введите

gradlew gwtcompile devmode

и получится, что он запустит полностью функциональный песочница (серверная сторона с реальной сохранностью файла должна работать)

Ответ 7

Вы можете использовать GwtSwfExt, который является оболочкой поверх SWFUpload (так же, как Swfupload-gwt lib), вы можете загрузить пример и исходный код из http://code.google.com/p/gwtswfext.

Ответ 8

Когда вы создаете свой собственный процесс загрузки файлов, вместо того, чтобы вытаскивать его из сервера через небольшое время, вы можете заставить клиента отображать неопределенную полосу в течение 2 секунд и вычислить расчетное время завершения, чтобы окончательное изменение и вытаскивать новые оценки каждые 5, 10 секунд вместо этого. что должно почти не влиять на трафик.