123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- package com.vaadin.client.extensions;
-
- import java.util.HashMap;
- import java.util.Map;
-
- import com.google.gwt.core.client.Scheduler;
- import com.google.gwt.dom.client.DataTransfer;
- import com.google.gwt.dom.client.NativeEvent;
- import com.google.gwt.xhr.client.XMLHttpRequest;
- import com.vaadin.shared.ui.Connect;
- import com.vaadin.shared.ui.dnd.FileDropTargetClientRpc;
- import com.vaadin.shared.ui.dnd.FileDropTargetRpc;
- import com.vaadin.shared.ui.dnd.FileDropTargetState;
- import com.vaadin.shared.ui.dnd.FileParameters;
- import com.vaadin.ui.dnd.FileDropTarget;
-
- import elemental.events.Event;
- import elemental.html.File;
- import elemental.html.FileList;
-
- /**
- * Extension to add file drop target functionality to a widget. It allows
- * dropping files onto the widget and uploading the dropped files to the server.
- *
- * @author Vaadin Ltd
- * @since 8.1
- */
- @Connect(FileDropTarget.class)
- public class FileDropTargetConnector extends DropTargetExtensionConnector {
-
- /**
- * Contains files and their IDs that are waiting to be uploaded.
- */
- private Map<String, File> filesToUpload = new HashMap<>();
-
- /**
- * Contains file IDs and upload URLs.
- */
- private Map<String, String> uploadUrls = new HashMap<>();
-
- /**
- * Counting identifier for the files to be uploaded.
- */
- private int fileId = 0;
-
- /**
- * Indicates whether a file is being uploaded.
- */
- private boolean uploading = false;
-
- /**
- * Constructs file drop target connector.
- */
- public FileDropTargetConnector() {
- registerRpc(FileDropTargetClientRpc.class,
- (FileDropTargetClientRpc) urls -> {
- uploadUrls.putAll(urls);
- uploadNextFile();
- });
- }
-
- /**
- * Uploads a file from the waiting list in case there are no files being
- * uploaded.
- */
- private void uploadNextFile() {
- Scheduler.get().scheduleDeferred(() -> {
- if (!uploading && !uploadUrls.isEmpty()) {
- uploading = true;
- String nextId = uploadUrls.keySet().stream().findAny().get();
-
- String url = uploadUrls.remove(nextId);
- File file = filesToUpload.remove(nextId);
-
- FileUploadXHR xhr = (FileUploadXHR) FileUploadXHR.create();
- xhr.setOnReadyStateChange(xmlHttpRequest -> {
- if (xmlHttpRequest.getReadyState() == XMLHttpRequest.DONE) {
- // Poll server for changes
- getRpcProxy(FileDropTargetRpc.class).poll();
- uploading = false;
- uploadNextFile();
- xmlHttpRequest.clearOnReadyStateChange();
- }
- });
- xhr.open("POST", getConnection().translateVaadinUri(url));
- xhr.postFile(file);
- }
- });
- }
-
- @Override
- protected void onDrop(Event event) {
- DataTransfer dataTransfer = ((NativeEvent) event).getDataTransfer();
- FileList files = getFiles(dataTransfer);
-
- if (files != null) {
- Map<String, FileParameters> fileParams = new HashMap<>();
- for (int i = 0; i < files.getLength(); i++) {
- File file = files.item(i);
-
- // Make sure the item is indeed a file and not a folder
- if (isFile(file, i, dataTransfer)) {
- String id = String.valueOf(++this.fileId);
-
- filesToUpload.put(id, file);
- fileParams.put(id, new FileParameters(file.getName(),
- (long) file.getSize(), file.getType()));
- }
- }
-
- // Request a list of upload URLs for the dropped files
- if (!fileParams.isEmpty()) {
- getRpcProxy(FileDropTargetRpc.class).drop(fileParams);
- }
-
- event.preventDefault();
- event.stopPropagation();
- }
-
- removeDragOverStyle((NativeEvent) event);
- }
-
- @Override
- public FileDropTargetState getState() {
- return (FileDropTargetState) super.getState();
- }
-
- /**
- * Returns the files parameter of the dataTransfer object.
- *
- * @param dataTransfer
- * DataTransfer object to retrieve files from.
- * @return {@code DataTransfer.files} parameter of the given dataTransfer
- * object.
- */
- private native FileList getFiles(DataTransfer dataTransfer)
- /*-{
- return dataTransfer.files;
- }-*/;
-
- /**
- * Checks whether the file on the given index is indeed a file or a folder.
- *
- * @param file
- * File object to prove it is not a folder.
- * @param fileIndex
- * Index of the file object.
- * @param dataTransfer
- * DataTransfer object that contains the list of files.
- * @return {@code true} if the given file at the given index is not a
- * folder, {@code false} otherwise.
- */
- private native boolean isFile(File file, int fileIndex,
- DataTransfer dataTransfer)
- /*-{
- // Chrome >= v21 and Opera >= v?
- if (dataTransfer.items) {
- var item = dataTransfer.items[fileIndex];
- if (typeof item.webkitGetAsEntry == "function") {
- var entry = item.webkitGetAsEntry();
- if (typeof entry !== "undefined" && entry !== null) {
- return entry.isFile;
- }
- }
- }
-
- // Zero sized files without a type are also likely to be folders
- if (file.size == 0 && !file.type) {
- return false;
- }
-
- // TODO Make it detect folders on all browsers
-
- return true;
- }-*/;
-
- /**
- * XHR that is used for uploading a file to the server.
- */
- private static class FileUploadXHR extends XMLHttpRequest {
-
- protected FileUploadXHR() {
- }
-
- public final native void postFile(File file)
- /*-{
- this.setRequestHeader('Content-Type', 'multipart/form-data');
- this.send(file);
- }-*/;
-
- }
- }
|