diff options
author | Adam Wagner <wbadam@users.noreply.github.com> | 2017-05-11 13:13:10 +0300 |
---|---|---|
committer | Pekka Hyvönen <pekka@vaadin.com> | 2017-05-11 13:13:10 +0300 |
commit | e2e3058a497f43f34f2fcfadf6b63de9211be659 (patch) | |
tree | 265632d4dfce4edb0fc15365e4f0339333dbcce3 | |
parent | a4ffc1e1597e2ce3a3ac2977458c8df25e112c88 (diff) | |
download | vaadin-framework-e2e3058a497f43f34f2fcfadf6b63de9211be659.tar.gz vaadin-framework-e2e3058a497f43f34f2fcfadf6b63de9211be659.zip |
Make it possible to upload files by dropping them onto a drop target (#9277)
Fixes #8891
10 files changed, 1008 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); + }-*/; + + } +} diff --git a/documentation/advanced/advanced-dragndrop.asciidoc b/documentation/advanced/advanced-dragndrop.asciidoc index 57764b55dd..6390eff201 100644 --- a/documentation/advanced/advanced-dragndrop.asciidoc +++ b/documentation/advanced/advanced-dragndrop.asciidoc @@ -287,4 +287,68 @@ When dragging data over a drop target Grid's row, depending on the drop mode and (((range="endofrange", startref="term.advanced.dragndrop"))) +== Drag and Drop Files +Files can be uploaded to the server by dropping them onto a file drop target. To make a component a file drop target, apply the [classname]#FileDropTarget# extension to it by creating a new instance and passing the component as first constructor parameter to it. + +You can handle the dropped files with the `FileDropHandler` that you add as the second constructor parameter. The [classname]#FileDropEvent#, received by the handler, contains information about the dropped files such as file name, file size and mime type. +In the handler you can decide if you would like to upload each of the dropped files. + +To start uploading a file, set a `StreamVariable` to it. The stream variable provides an output stream where the file will be written and has callback methods for all the stages of the upload process. + +[source,java] +---- +Label dropArea = new Label("Drop files here"); +FileDropTarget<Label> dropTarget = new FileDropTarget<>(dropArea, event -> { + + List<Html5File> files = event.getFiles(); + files.forEach(file -> { + // Max 1 MB files are uploaded + if (file.getFileSize() <= 1024 * 1024) { + file.setStreamVariable(new StreamVariable() { + + // Output stream to write the file to + @Override + public OutputStream getOutputStream() { + return new FileOutputStream("/path/to/files/" + + file.getFileName()); + } + + // Returns whether onProgress() is called during upload + @Override + public boolean listenProgress() { + return true; + } + + // Called periodically during upload + @Override + public void onProgress(StreamingProgressEvent event) { + Notification.show("Progress, bytesReceived=" + + event.getBytesReceived()); + } + + // Called when upload started + @Override + public void streamingStarted(StreamingStartEvent event) { + Notification.show("Stream started, fileName=" + + event.getFileName()); + } + + // Called when upload finished + @Override + public void streamingFinished(StreamingEndEvent event) { + Notification.show("Stream finished, fileName=" + + event.getFileName()); + } + + // Called when upload failed + @Override + public void streamingFailed(StreamingErrorEvent event) { + Notification.show("Stream failed, fileName=" + + event.getFileName()); + } + }); + } + } +}); +---- diff --git a/server/src/main/java/com/vaadin/event/dnd/FileDropEvent.java b/server/src/main/java/com/vaadin/event/dnd/FileDropEvent.java new file mode 100644 index 0000000000..42d270ebe4 --- /dev/null +++ b/server/src/main/java/com/vaadin/event/dnd/FileDropEvent.java @@ -0,0 +1,62 @@ +/* + * 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.event.dnd; + +import java.util.List; + +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Component; +import com.vaadin.ui.Html5File; + +/** + * File drop event that contains the list of files dropped on a file drop + * target. + * + * @param <T> + * Type of the file drop target component. + * @author Vaadin Ltd + * @see FileDropHandler + * @since 8.1 + */ +public class FileDropEvent<T extends AbstractComponent> extends + Component.Event { + + private final List<Html5File> files; + + /** + * Creates a file drop event. + * + * @param target + * The file drop target component. + * @param files + * List of files. + */ + public FileDropEvent(T target, List<Html5File> files) { + super(target); + + this.files = files; + } + + /** + * Gets the list of files dropped onto the file drop target component. + * + * @return List of files that were dropped onto the file drop target + * component. + */ + public List<Html5File> getFiles() { + return files; + } +} diff --git a/server/src/main/java/com/vaadin/event/dnd/FileDropHandler.java b/server/src/main/java/com/vaadin/event/dnd/FileDropHandler.java new file mode 100644 index 0000000000..5e74e44349 --- /dev/null +++ b/server/src/main/java/com/vaadin/event/dnd/FileDropHandler.java @@ -0,0 +1,44 @@ +/* + * 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.event.dnd; + +import java.io.Serializable; + +import com.vaadin.ui.AbstractComponent; + +/** + * Handles the drop event on a file drop target. + * + * @param <T> + * Type of the file drop target component. + * @author Vaadin Ltd + * @see FileDropEvent + * @see com.vaadin.ui.FileDropTarget + * @since 8.1 + */ +public interface FileDropHandler<T extends AbstractComponent> extends + Serializable { + + /** + * Handles the drop event. The method is called when files are dropped onto + * the file drop target this handler is registered to. + * + * @param event + * The file drop event containing the list of files that were + * dropped onto the component. + */ + public void drop(FileDropEvent<T> event); +} diff --git a/server/src/main/java/com/vaadin/ui/FileDropTarget.java b/server/src/main/java/com/vaadin/ui/FileDropTarget.java new file mode 100644 index 0000000000..654563dbd0 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/FileDropTarget.java @@ -0,0 +1,262 @@ +/* + * 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.ui; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.vaadin.event.dnd.DropTargetExtension; +import com.vaadin.event.dnd.FileDropEvent; +import com.vaadin.event.dnd.FileDropHandler; +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.StreamVariable; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.ui.dnd.FileDropTargetClientRpc; +import com.vaadin.shared.ui.dnd.FileDropTargetRpc; +import com.vaadin.shared.ui.dnd.FileDropTargetState; + +/** + * Extension to add drop target functionality to a widget for accepting and + * uploading files. + * + * @param <T> + * Type of the component to be extended. + * @author Vaadin Ltd + * @since 8.1 + */ +public class FileDropTarget<T extends AbstractComponent> extends + DropTargetExtension<T> { + + /** + * Handles the file drop event. + */ + private final FileDropHandler<T> fileDropHandler; + + /** + * Extends {@code target} component and makes it a file drop target. A file + * drop handler needs to be added to handle the file drop event. + * + * @param target + * Component to be extended. + * @param fileDropHandler + * File drop handler that handles the file drop event. + * @see FileDropEvent + */ + public FileDropTarget(T target, FileDropHandler<T> fileDropHandler) { + super(target); + + this.fileDropHandler = fileDropHandler; + } + + @Override + protected void registerDropTargetRpc(T target) { + super.registerDropTargetRpc(target); + + registerRpc((FileDropTargetRpc) fileParams -> { + + List<Html5File> files = new ArrayList<>(); + Map<String, String> urls = new HashMap<>(); + + fileParams.forEach((id, fileParameters) -> { + Html5File html5File = new Html5File(fileParameters.getName(), + fileParameters.getSize(), fileParameters.getMime()); + String url = createUrl(html5File, id); + + files.add(html5File); + urls.put(id, url); + }); + + getRpcProxy(FileDropTargetClientRpc.class).sendUploadUrl(urls); + + FileDropEvent<T> event = new FileDropEvent<>(target, files); + fileDropHandler.drop(event); + }); + } + + /** + * Creates an upload URL for the given file and file ID. + * + * @param file + * File to be uploaded. + * @param id + * Generated ID for the file. + * @return Upload URL for uploading the file to the server. + */ + private String createUrl(Html5File file, String id) { + return getStreamVariableTargetUrl("rec-" + id, + new FileReceiver(id, file)); + } + + private String getStreamVariableTargetUrl(String name, + StreamVariable value) { + String connectorId = getConnectorId(); + UI ui = getUI(); + int uiId = ui.getUIId(); + String key = uiId + "/" + connectorId + "/" + name; + + ConnectorTracker connectorTracker = ui.getConnectorTracker(); + connectorTracker.addStreamVariable(connectorId, name, value); + String secKey = connectorTracker.getSeckey(value); + + return ApplicationConstants.APP_PROTOCOL_PREFIX + + ServletPortletHelper.UPLOAD_URL_PREFIX + key + "/" + secKey; + } + + @Override + protected FileDropTargetState getState() { + return (FileDropTargetState) super.getState(); + } + + + @Override + protected FileDropTargetState getState(boolean markAsDirty) { + return (FileDropTargetState) super.getState(markAsDirty); + } + + /** + * Returns the component this extension is attached to. + * + * @return Extended component. + */ + @Override + @SuppressWarnings("unchecked") + public T getParent() { + return (T) super.getParent(); + } + + private class FileReceiver implements StreamVariable { + + private final String id; + private Html5File file; + + public FileReceiver(String id, Html5File file) { + this.id = id; + this.file = file; + } + + private boolean listenProgressOfUploadedFile; + + @Override + public OutputStream getOutputStream() { + if (file.getStreamVariable() == null) { + return null; + } + return file.getStreamVariable().getOutputStream(); + } + + @Override + public boolean listenProgress() { + return file.getStreamVariable().listenProgress(); + } + + @Override + public void onProgress(StreamingProgressEvent event) { + file.getStreamVariable() + .onProgress(new ReceivingEventWrapper(event)); + } + + @Override + public void streamingStarted(StreamingStartEvent event) { + listenProgressOfUploadedFile = file.getStreamVariable() != null; + if (listenProgressOfUploadedFile) { + file.getStreamVariable() + .streamingStarted(new ReceivingEventWrapper(event)); + } + } + + @Override + public void streamingFinished(StreamingEndEvent event) { + if (listenProgressOfUploadedFile) { + file.getStreamVariable() + .streamingFinished(new ReceivingEventWrapper(event)); + } + } + + @Override + public void streamingFailed(final StreamingErrorEvent event) { + if (listenProgressOfUploadedFile) { + file.getStreamVariable() + .streamingFailed(new ReceivingEventWrapper(event)); + } + } + + @Override + public boolean isInterrupted() { + return file.getStreamVariable().isInterrupted(); + } + + /* + * With XHR2 file posts we can't provide as much information from the + * terminal as with multipart request. This helper class wraps the + * terminal event and provides the lacking information from the + * FileParameters. + */ + class ReceivingEventWrapper implements StreamingErrorEvent, + StreamingEndEvent, StreamingStartEvent, StreamingProgressEvent { + + private final StreamingEvent wrappedEvent; + + ReceivingEventWrapper(StreamingEvent e) { + wrappedEvent = e; + } + + @Override + public String getMimeType() { + return file.getType(); + } + + @Override + public String getFileName() { + return file.getFileName(); + } + + @Override + public long getContentLength() { + return file.getFileSize(); + } + + public StreamVariable getReceiver() { + return FileReceiver.this; + } + + @Override + public Exception getException() { + if (wrappedEvent instanceof StreamingErrorEvent) { + return ((StreamingErrorEvent) wrappedEvent).getException(); + } + return null; + } + + @Override + public long getBytesReceived() { + return wrappedEvent.getBytesReceived(); + } + + /** + * Calling this method has no effect. DD files are receive only once + * anyway. + */ + @Override + public void disposeStreamVariable() { + + } + } + + } +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetClientRpc.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetClientRpc.java new file mode 100644 index 0000000000..479e026cf7 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetClientRpc.java @@ -0,0 +1,37 @@ +/* + * 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.shared.ui.dnd; + +import java.util.Map; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * RPC for sending the upload URLs to the client for uploading files. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public interface FileDropTargetClientRpc extends ClientRpc { + + /** + * Sends the of upload URLs mapped to the generated file ID. + * + * @param urls + * File IDs and URLs for uploading files to the server. + */ + void sendUploadUrl(Map<String, String> urls); +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetRpc.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetRpc.java new file mode 100644 index 0000000000..fd290a41f5 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetRpc.java @@ -0,0 +1,37 @@ +/* + * 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.shared.ui.dnd; + +import java.util.Map; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * RPC for requesting upload URLs for files dropped on the file drop target. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public interface FileDropTargetRpc extends ServerRpc { + + /** + * Called when files are dropped onto the file drop target. + * + * @param fileParams + * Generated file IDs and file parameters of dropped files. + */ + public void drop(Map<String, FileParameters> fileParams); +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetState.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetState.java new file mode 100644 index 0000000000..d825a4d60f --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileDropTargetState.java @@ -0,0 +1,26 @@ +/* + * 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.shared.ui.dnd; + +/** + * State class containing parameters for FileDropTarget. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public class FileDropTargetState extends DropTargetState { + +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/dnd/FileParameters.java b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileParameters.java new file mode 100644 index 0000000000..0e4f5cdf76 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/dnd/FileParameters.java @@ -0,0 +1,110 @@ +/* + * 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.shared.ui.dnd; + +import java.io.Serializable; + +/** + * Contains parameters of a file. Used for transferring information about + * dropped files from the client to the server. + * + * @author Vaadin Ltd + * @since 8.1 + */ +public class FileParameters implements Serializable { + private String name; + private long size; + private String mime; + + /** + * Creates a file parameters object. + */ + public FileParameters() { + } + + /** + * Creates a file parameters object. + * + * @param name + * Name of the file. + * @param size + * Size of the file. + * @param mime + * Mime type of the file. + */ + public FileParameters(String name, long size, String mime) { + this.name = name; + this.size = size; + this.mime = mime; + } + + /** + * Gets the file name. + * + * @return Name of the file. + */ + public String getName() { + return name; + } + + /** + * Sets the file name. + * + * @param name + * Name of the file. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the file size. + * + * @return Size of the file. + */ + public long getSize() { + return size; + } + + /** + * Sets the file size. + * + * @param size + * Size of the file. + */ + public void setSize(long size) { + this.size = size; + } + + /** + * Gets the mime type. + * + * @return Mime type of the file. + */ + public String getMime() { + return mime; + } + + /** + * Sets the mime type. + * + * @param mime + * Mime type of the file. + */ + public void setMime(String mime) { + this.mime = mime; + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/dnd/Html5FileDragAndDropUpload.java b/uitest/src/main/java/com/vaadin/tests/dnd/Html5FileDragAndDropUpload.java new file mode 100644 index 0000000000..211d50d047 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/dnd/Html5FileDragAndDropUpload.java @@ -0,0 +1,164 @@ +/* + * 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.tests.dnd; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.server.StreamVariable; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.dnd.FileParameters; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.FileDropTarget; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Html5File; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.VerticalLayout; + +public class Html5FileDragAndDropUpload extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + + Label dropArea = new Label("Drop files here"); + + FileDropTarget<Label> dropTarget = new FileDropTarget<>(dropArea, + event -> { + List<Html5File> files = event.getFiles(); + if (files != null) { + files.forEach(file -> { + file.setStreamVariable(new StreamVariable() { + @Override + public OutputStream getOutputStream() { + return new OutputStream() { + @Override + public void write(int b) throws + IOException { + // NOP + } + }; + } + + @Override + public boolean listenProgress() { + return true; + } + + @Override + public void onProgress( + StreamingProgressEvent event) { + log("Progress, bytesReceived=" + event + .getBytesReceived()); + } + + @Override + public void streamingStarted( + StreamingStartEvent event) { + log("Stream started, fileName=" + event + .getFileName()); + } + + @Override + public void streamingFinished( + StreamingEndEvent event) { + log("Stream finished, fileName=" + event + .getFileName()); + } + + @Override + public void streamingFailed( + StreamingErrorEvent event) { + log("Stream failed, fileName=" + event + .getFileName()); + } + + @Override + public boolean isInterrupted() { + return false; + } + }); + }); + } + }); + + Grid<FileParameters> grid = new Grid<>(); + grid.addColumn(FileParameters::getName).setCaption("File name"); + grid.addColumn(FileParameters::getSize).setCaption("File size"); + grid.addColumn(FileParameters::getMime).setCaption("Mime type"); + + List<FileParameters> gridItems = new ArrayList<>(); + + new FileDropTarget<Grid<FileParameters>>(grid, event -> { + event.getFiles().forEach(html5File -> { + html5File.setStreamVariable(new StreamVariable() { + @Override + public OutputStream getOutputStream() { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + // NOP + } + }; + } + + @Override + public boolean listenProgress() { + return false; + } + + @Override + public void onProgress(StreamingProgressEvent event) { + // NOP + } + + @Override + public void streamingStarted(StreamingStartEvent event) { + // NOP + } + + @Override + public void streamingFinished(StreamingEndEvent event) { + gridItems.add(new FileParameters(event.getFileName(), + event.getContentLength(), event.getMimeType())); + grid.setItems(gridItems); + } + + @Override + public void streamingFailed(StreamingErrorEvent event) { + + } + + @Override + public boolean isInterrupted() { + return false; + } + }); + }); + }); + + Layout layout = new VerticalLayout(dropArea, grid); + + addComponent(layout); + } + + @Override + protected String getTestDescription() { + return "Drop and upload files onto file drop target"; + } +} |