com.vaadin.client.extensions.FileDropTargetConnector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-client Show documentation
Show all versions of vaadin-client Show documentation
Vaadin is a web application framework for Rich Internet Applications (RIA).
Vaadin enables easy development and maintenance of fast and
secure rich web
applications with a stunning look and feel and a wide browser support.
It features a server-side architecture with the majority of the logic
running
on the server. Ajax technology is used at the browser-side to ensure a
rich
and interactive user experience.
/*
* Copyright 2000-2021 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 filesToUpload = new HashMap<>();
/**
* Contains file IDs and upload URLs.
*/
private Map 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 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)
/*-{
var formData = new $wnd.FormData();
formData.append("File", file);
this.send(formData);
}-*/;
}
}