Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

FileDropTargetConnector.java 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.extensions;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. import com.google.gwt.core.client.Scheduler;
  20. import com.google.gwt.dom.client.DataTransfer;
  21. import com.google.gwt.dom.client.NativeEvent;
  22. import com.google.gwt.xhr.client.XMLHttpRequest;
  23. import com.vaadin.shared.ui.Connect;
  24. import com.vaadin.shared.ui.dnd.FileDropTargetClientRpc;
  25. import com.vaadin.shared.ui.dnd.FileDropTargetRpc;
  26. import com.vaadin.shared.ui.dnd.FileDropTargetState;
  27. import com.vaadin.shared.ui.dnd.FileParameters;
  28. import com.vaadin.ui.dnd.FileDropTarget;
  29. import elemental.events.Event;
  30. import elemental.html.File;
  31. import elemental.html.FileList;
  32. /**
  33. * Extension to add file drop target functionality to a widget. It allows
  34. * dropping files onto the widget and uploading the dropped files to the server.
  35. *
  36. * @author Vaadin Ltd
  37. * @since 8.1
  38. */
  39. @Connect(FileDropTarget.class)
  40. public class FileDropTargetConnector extends DropTargetExtensionConnector {
  41. /**
  42. * Contains files and their IDs that are waiting to be uploaded.
  43. */
  44. private Map<String, File> filesToUpload = new HashMap<>();
  45. /**
  46. * Contains file IDs and upload URLs.
  47. */
  48. private Map<String, String> uploadUrls = new HashMap<>();
  49. /**
  50. * Counting identifier for the files to be uploaded.
  51. */
  52. private int fileId = 0;
  53. /**
  54. * Indicates whether a file is being uploaded.
  55. */
  56. private boolean uploading = false;
  57. /**
  58. * Constructs file drop target connector.
  59. */
  60. public FileDropTargetConnector() {
  61. registerRpc(FileDropTargetClientRpc.class,
  62. (FileDropTargetClientRpc) urls -> {
  63. uploadUrls.putAll(urls);
  64. uploadNextFile();
  65. });
  66. }
  67. /**
  68. * Uploads a file from the waiting list in case there are no files being
  69. * uploaded.
  70. */
  71. private void uploadNextFile() {
  72. Scheduler.get().scheduleDeferred(() -> {
  73. if (!uploading && !uploadUrls.isEmpty()) {
  74. uploading = true;
  75. String nextId = uploadUrls.keySet().stream().findAny().get();
  76. String url = uploadUrls.remove(nextId);
  77. File file = filesToUpload.remove(nextId);
  78. FileUploadXHR xhr = (FileUploadXHR) FileUploadXHR.create();
  79. xhr.setOnReadyStateChange(xmlHttpRequest -> {
  80. if (xmlHttpRequest.getReadyState() == XMLHttpRequest.DONE) {
  81. // Poll server for changes
  82. getRpcProxy(FileDropTargetRpc.class).poll();
  83. uploading = false;
  84. uploadNextFile();
  85. xmlHttpRequest.clearOnReadyStateChange();
  86. }
  87. });
  88. xhr.open("POST", getConnection().translateVaadinUri(url));
  89. xhr.postFile(file);
  90. }
  91. });
  92. }
  93. @Override
  94. protected void onDrop(Event event) {
  95. DataTransfer dataTransfer = ((NativeEvent) event).getDataTransfer();
  96. FileList files = getFiles(dataTransfer);
  97. if (files != null) {
  98. Map<String, FileParameters> fileParams = new HashMap<>();
  99. for (int i = 0; i < files.getLength(); i++) {
  100. File file = files.item(i);
  101. // Make sure the item is indeed a file and not a folder
  102. if (isFile(file, i, dataTransfer)) {
  103. String id = String.valueOf(++this.fileId);
  104. filesToUpload.put(id, file);
  105. fileParams.put(id, new FileParameters(file.getName(),
  106. (long) file.getSize(), file.getType()));
  107. }
  108. }
  109. // Request a list of upload URLs for the dropped files
  110. if (!fileParams.isEmpty()) {
  111. getRpcProxy(FileDropTargetRpc.class).drop(fileParams);
  112. }
  113. event.preventDefault();
  114. event.stopPropagation();
  115. }
  116. removeDragOverStyle((NativeEvent) event);
  117. }
  118. @Override
  119. public FileDropTargetState getState() {
  120. return (FileDropTargetState) super.getState();
  121. }
  122. /**
  123. * Returns the files parameter of the dataTransfer object.
  124. *
  125. * @param dataTransfer
  126. * DataTransfer object to retrieve files from.
  127. * @return {@code DataTransfer.files} parameter of the given dataTransfer
  128. * object.
  129. */
  130. private native FileList getFiles(DataTransfer dataTransfer)
  131. /*-{
  132. return dataTransfer.files;
  133. }-*/;
  134. /**
  135. * Checks whether the file on the given index is indeed a file or a folder.
  136. *
  137. * @param file
  138. * File object to prove it is not a folder.
  139. * @param fileIndex
  140. * Index of the file object.
  141. * @param dataTransfer
  142. * DataTransfer object that contains the list of files.
  143. * @return {@code true} if the given file at the given index is not a
  144. * folder, {@code false} otherwise.
  145. */
  146. private native boolean isFile(File file, int fileIndex,
  147. DataTransfer dataTransfer)
  148. /*-{
  149. // Chrome >= v21 and Opera >= v?
  150. if (dataTransfer.items) {
  151. var item = dataTransfer.items[fileIndex];
  152. if (typeof item.webkitGetAsEntry == "function") {
  153. var entry = item.webkitGetAsEntry();
  154. if (typeof entry !== "undefined" && entry !== null) {
  155. return entry.isFile;
  156. }
  157. }
  158. }
  159. // Zero sized files without a type are also likely to be folders
  160. if (file.size == 0 && !file.type) {
  161. return false;
  162. }
  163. // TODO Make it detect folders on all browsers
  164. return true;
  165. }-*/;
  166. /**
  167. * XHR that is used for uploading a file to the server.
  168. */
  169. private static class FileUploadXHR extends XMLHttpRequest {
  170. protected FileUploadXHR() {
  171. }
  172. public final native void postFile(File file)
  173. /*-{
  174. this.setRequestHeader('Content-Type', 'multipart/form-data');
  175. this.send(file);
  176. }-*/;
  177. }
  178. }