aboutsummaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorAdam Wagner <wbadam@users.noreply.github.com>2017-05-11 13:13:10 +0300
committerPekka Hyvönen <pekka@vaadin.com>2017-05-11 13:13:10 +0300
commite2e3058a497f43f34f2fcfadf6b63de9211be659 (patch)
tree265632d4dfce4edb0fc15365e4f0339333dbcce3 /client
parenta4ffc1e1597e2ce3a3ac2977458c8df25e112c88 (diff)
downloadvaadin-framework-e2e3058a497f43f34f2fcfadf6b63de9211be659.tar.gz
vaadin-framework-e2e3058a497f43f34f2fcfadf6b63de9211be659.zip
Make it possible to upload files by dropping them onto a drop target (#9277)
Fixes #8891
Diffstat (limited to 'client')
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/FileDropTargetConnector.java202
1 files changed, 202 insertions, 0 deletions
diff --git a/client/src/main/java/com/vaadin/client/extensions/FileDropTargetConnector.java b/client/src/main/java/com/vaadin/client/extensions/FileDropTargetConnector.java
new file mode 100644
index 0000000000..f496e90d7f
--- /dev/null
+++ b/client/src/main/java/com/vaadin/client/extensions/FileDropTargetConnector.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2000-2016 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.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.size() > 0) {
+ 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) {
+ 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.size() > 0) {
+ getRpcProxy(FileDropTargetRpc.class).drop(fileParams);
+ }
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ @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);
+ }-*/;
+
+ }
+}