summaryrefslogtreecommitdiffstats
path: root/server
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 /server
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 'server')
-rw-r--r--server/src/main/java/com/vaadin/event/dnd/FileDropEvent.java62
-rw-r--r--server/src/main/java/com/vaadin/event/dnd/FileDropHandler.java44
-rw-r--r--server/src/main/java/com/vaadin/ui/FileDropTarget.java262
3 files changed, 368 insertions, 0 deletions
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() {
+
+ }
+ }
+
+ }
+}