Skip to content
This repository

Drag & Drop support of folders (Chrome >= 21) #587

Open
wants to merge 9 commits into from

5 participants

Christopher Blum Jellyfrog frankbolviken Davit Barbakadze Arcara
Christopher Blum
tiff commented July 09, 2012

Chrome 21 implemented the possibility for folder drag & drop.

This patch adds a "relativePath" property to each instance of plupload.File.

Example: http://protonet.github.com/plupload/examples/drag_and_drop.html
More details to the chrome implementation: http://wiki.whatwg.org/wiki/DragAndDropEntries
Chromium issue: http://code.google.com/p/chromium/issues/detail?id=99823

Also this patch makes sure that the default behavior within dragover is only prevented when the drag contains any files otherwise there will be problems when eg. the drop_element is a <textarea> and the user is dragging text around.

frankbolviken

What's the progress on this one? We need this functionality.

Davit Barbakadze
Owner

Checking if those are indeed files that are being dragged is interesting. Will bear this in mind.

Arcara

Hi

I would like to know if the folder uploading function will be incorporated to plupload 2, and why it's not in the latest releases of plupload 1.5.7 although the code is here. I'm trying to add it to this one, but I'm not able to add the files of a folder to the plupload component.

Thank you very much,
Carlos

Davit Barbakadze
Owner

It is incorporated in Plupload 2.

Regarding this code here, it is good, but still has some flaws. So that we cannot merge it directly. But more importantly the code for our html5 runtime in 1.x branch is already quite big and hard to follow. We decided to not extend it any further. So, only bugfixes. Plupload 2 will get all new features.

Arcara

If not too much to ask, do you know approximately when it will be available in the main branch of plupload 2?
It's very useful and I'm hesitating between waiting it or programming me on the server side another way to upload files keeping the folders structure (ej: uploading a zip file and uncompress it).

Thank you very much for your attention and congratulations for your work, plupload is the best upload component that I know.

Davit Barbakadze
Owner

It is in main branch already. The latest binaries are always available at moxie repo, ubder bin/ folder.

Arcara

I'm seeing the code, and I see the calls to webkitGetAsEntry and else, but I don't see any reference of relativePath or any other way to know the path of each file (of course, this is null when drag&drop folder and its files). Can it be done at present?

Thank you very much

Davit Barbakadze
Owner

Dropped folders are recursively checked for files that fulfill filter requirements and files that fit are added. That's it. How you planned to use relativePath or where did you expect it to appear?

Arcara

Yes, when I drag & drop a folder with subfolders and files inside, all the files appear in plupload, but I need to know what is the folder structure of each one of the files to uploading in order to I can clone the whole folder structure (with its files) in my server.
So, I thought that each one of the files in the array 'uploader.files' had a property '.relativePath' or similar in that manner I could do something like this:

jQuery('#uploader').plupload('getUploader').bind('BeforeUpload', function (up, file) {
       up.settings.multipart_params = {'originalFileName':file.name,'totalSize':file.size,'relativePath':file.relativePath}
});
Davit Barbakadze
Owner

Makes sense. Also shouldn't require too much coding to add I guess, I'll check...

Davit Barbakadze
Owner

After some thinking I came to a conclusion that it is more confusing than useful, mainly because it is not supported across browsers. Not sure why you would want to rely on something like that?

Arcara

It would be very useful and the easiest way to be able to upload files from different folders without mixing them and preserving the original order (that sometimes is complex and shouldn't be lost).
In many cases is necessary preserve in the server the same folder structure that in the CD/DVD/USB/HDD of the computer (I.E: to separate docs from videos and photos, and inside each one, to maintain all the subfolders with the date or place in wich they were made).
At present and without this function, to preserve the folder structure, firstly I have to create manually, one by one, all the folder structure (it can be big), and later go with plupload to each folder for uploading all its files, and go on with the next folder.
Although at the moment it only works with Chrome, it would be the only but at the same time a great way to reach that objetive much more easily and quickly.
Of course, if anyone use other browser different than Chrome (and I hope firefox provide in the future something similar to webkitGetAsEntry), .relativePath would be null and would have no other option that put all files in the same target folder.

Thank you very much for your attention
Carlos

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
89  examples/drag_and_drop.html
... ...
@@ -0,0 +1,89 @@
  1
+<!DOCTYPE html>
  2
+<html>
  3
+  <head>
  4
+    <meta charset="utf-8">
  5
+    <title>Plupload - Drag &amp; drop example</title>
  6
+    <style>
  7
+      body {
  8
+        font-family: Verdana, Geneva, sans-serif;
  9
+        font-size: 13px;
  10
+        color: #333;
  11
+        background: url(bg.jpg);
  12
+        width: 620px;
  13
+        margin: 40px auto;
  14
+      }
  15
+      #drop-target {
  16
+        border: 10px dashed #999;
  17
+        text-align: center;
  18
+        color: #999;
  19
+        font-size: 20px;
  20
+        width: 600px;
  21
+        height: 300px;
  22
+        line-height: 300px;
  23
+        cursor: pointer;
  24
+      }
  25
+      
  26
+      #drop-target.dragover {
  27
+        background: rgba(255, 255, 255, 0.4);
  28
+        border-color: green;
  29
+      }
  30
+      
  31
+      #debug {
  32
+        margin-top: 20px;
  33
+      }
  34
+    </style>
  35
+  </head>
  36
+  <body>
  37
+    <div id="drop-target">Drop your files or folders (Chrome >= 21) here</div>
  38
+    
  39
+    <div id="debug">No runtime found, your browser doesn't support HTML5 drag &amp; drop upload.</div>
  40
+    <script type="text/javascript" src="../src/javascript/plupload.js"></script>
  41
+    <script type="text/javascript" src="../src/javascript/plupload.html5.js"></script>
  42
+    <script type="text/javascript">
  43
+      // Custom example logic
  44
+      function $(id) {
  45
+        return document.getElementById(id);
  46
+      }
  47
+    
  48
+      var uploader = new plupload.Uploader({
  49
+        runtimes : 'html5',
  50
+        drop_element : 'drop-target',
  51
+        browse_button : 'drop-target',
  52
+        max_file_size : '10mb',
  53
+        upload : "upload.php"
  54
+      });
  55
+      
  56
+      uploader.bind('Init', function(up, params) {
  57
+        if (uploader.features.dragdrop) {
  58
+          $('debug').innerHTML = "";
  59
+          
  60
+          var target = $("drop-target");
  61
+          
  62
+          target.ondragover = function(event) {
  63
+            event.dataTransfer.dropEffect = "copy";
  64
+          };
  65
+          
  66
+          target.ondragenter = function() {
  67
+            this.className = "dragover";
  68
+          };
  69
+          
  70
+          target.ondragleave = function() {
  71
+            this.className = "";
  72
+          };
  73
+          
  74
+          target.ondrop = function() {
  75
+            this.className = "";
  76
+          };
  77
+        }
  78
+      });
  79
+
  80
+      uploader.bind('FilesAdded', function(up, files) {
  81
+        for (var i in files) {
  82
+          $('debug').innerHTML += '<div id="' + files[i].id + '">- ' + files[i].relativePath + ' (' + plupload.formatSize(files[i].size) + ')</div>';
  83
+        }
  84
+      });
  85
+
  86
+      uploader.init();
  87
+    </script>
  88
+  </body>
  89
+</html>
91  src/javascript/plupload.html5.js
@@ -211,27 +211,80 @@
211 211
 		 */
212 212
 		init : function(uploader, callback) {
213 213
 			var features, xhr;
214  
-
  214
+			
  215
+			function hasFiles(dataTransfer) {
  216
+				if (!dataTransfer || typeof(dataTransfer.files) === "undefined") {
  217
+					return false;
  218
+				}
  219
+				
  220
+				var types = plupload.toArray(dataTransfer.types || []);
  221
+				return types.indexOf("public.file-url") !== -1 || // Safari < 5
  222
+					types.indexOf("application/x-moz-file") !== -1 || // Gecko < 1.9.2 (< Firefox 3.6)
  223
+					types.indexOf("Files") !== -1 || // Standard
  224
+					types.length === 0;
  225
+			}
  226
+			
  227
+			function walkFileSystem(directory, callback, error) {
  228
+				if (!callback.pending) {
  229
+					callback.pending = 0;
  230
+				}
  231
+				if (!callback.files) {
  232
+					callback.files = [];
  233
+				}
  234
+				
  235
+				callback.pending++;
  236
+				
  237
+				var reader = directory.createReader(),
  238
+						relativePath = directory.fullPath.replace(/^\//, "").replace(/(.+?)\/?$/, "$1/");
  239
+				reader.readEntries(function(entries) {
  240
+					callback.pending--;
  241
+					plupload.each(entries, function(entry) {
  242
+						if (entry.isFile) {
  243
+							callback.pending++;
  244
+							entry.file(function(File) {
  245
+								File.relativePath = relativePath + File.name;
  246
+								callback.files.push(File);
  247
+								if (--callback.pending === 0) {
  248
+									callback(callback.files);
  249
+								}
  250
+							}, error);
  251
+						} else {
  252
+							walkFileSystem(entry, callback, error);
  253
+						}
  254
+					});
  255
+					
  256
+					if (callback.pending === 0) {
  257
+						callback(callback.files);
  258
+					}
  259
+				}, error);
  260
+			}
  261
+			
215 262
 			function addSelectedFiles(native_files) {
216  
-				var file, i, files = [], id, fileNames = {};
  263
+				var file, i, files = [], id, fileName, fileNames = {};
217 264
 
218 265
 				// Add the selected files to the file queue
219 266
 				for (i = 0; i < native_files.length; i++) {
220 267
 					file = native_files[i];
221  
-										
  268
+					fileName = file.fileName || file.name;
  269
+					
  270
+					// Safari on iOS 6 will name each picture "image.jpg"
  271
+					if (fileName === "image.jpg" && native_files.length > 1) {
  272
+	 					fileName = "image_" + (i + 1) + ".jpg";
  273
+					}
  274
+					
222 275
 					// Safari on Windows will add first file from dragged set multiple times
223 276
 					// @see: https://bugs.webkit.org/show_bug.cgi?id=37957
224  
-					if (fileNames[file.name]) {
  277
+					if (fileNames[fileName]) {
225 278
 						continue;
226 279
 					}
227  
-					fileNames[file.name] = true;
  280
+					fileNames[fileName] = true;
228 281
 
229 282
 					// Store away gears blob internally
230 283
 					id = plupload.guid();
231 284
 					html5files[id] = file;
232 285
 
233 286
 					// Expose id, name and size
234  
-					files.push(new plupload.File(id, file.fileName || file.name, file.fileSize || file.size)); // fileName / fileSize depricated
  287
+					files.push(new plupload.File(id, fileName, file.fileSize || file.size, file.relativePath)); // fileName / fileSize deprecated
235 288
 				}
236 289
 
237 290
 				// Trigger FilesAdded event if we added any
@@ -380,7 +433,7 @@
380 433
 
381 434
 							// Get or create drop zone
382 435
 							dropInputElm = document.getElementById(uploader.id + "_drop");
383  
-							if (!dropInputElm) {
  436
+							if (!dropInputElm && hasFiles(e.dataTransfer)) {
384 437
 								dropInputElm = document.createElement("input");
385 438
 								dropInputElm.setAttribute('type', "file");
386 439
 								dropInputElm.setAttribute('id', uploader.id + "_drop");
@@ -423,16 +476,30 @@
423 476
 
424 477
 					// Block browser default drag over
425 478
 					plupload.addEvent(dropElm, 'dragover', function(e) {
426  
-						e.preventDefault();
  479
+						if (hasFiles(e.dataTransfer)) {
  480
+							e.preventDefault();
  481
+						}
427 482
 					}, uploader.id);
428 483
 
429 484
 					// Attach drop handler and grab files
430 485
 					plupload.addEvent(dropElm, 'drop', function(e) {
431 486
 						var dataTransfer = e.dataTransfer;
432  
-
433  
-						// Add dropped files
434  
-						if (dataTransfer && dataTransfer.files) {
435  
-							addSelectedFiles(dataTransfer.files);
  487
+						
  488
+						if (!hasFiles(dataTransfer)) {
  489
+							return;
  490
+						}
  491
+						
  492
+						var items = dataTransfer.items || [], files = dataTransfer.files, firstEntry;
  493
+						if (items[0] && items[0].webkitGetAsEntry && (firstEntry = items[0].webkitGetAsEntry())) {
  494
+						  // Experimental way of uploading entire folders (only supported by chrome >= 21)
  495
+							walkFileSystem(firstEntry.filesystem.root, function(files) {
  496
+								addSelectedFiles(files);
  497
+							}, function() {
  498
+							  // Fallback to old way when error happens
  499
+								addSelectedFiles(files);
  500
+							});
  501
+						} else {
  502
+							addSelectedFiles(files);
436 503
 						}
437 504
 
438 505
 						e.preventDefault();
10  src/javascript/plupload.js
@@ -1603,7 +1603,7 @@
1603 1603
 	 * @param {String} name File name.
1604 1604
 	 * @param {Number} size File size in bytes.
1605 1605
 	 */
1606  
-	plupload.File = function(id, name, size) {
  1606
+	plupload.File = function(id, name, size, relativePath) {
1607 1607
 		var self = this; // Setup alias for self to reduce code size when it's compressed
1608 1608
 
1609 1609
 		/**
@@ -1631,6 +1631,14 @@
1631 1631
 		self.size = size;
1632 1632
 
1633 1633
 		/**
  1634
+		 * Relative path in original file system
  1635
+		 *
  1636
+		 * @property relativePath
  1637
+		 * @type String
  1638
+		 */
  1639
+		self.relativePath = relativePath || name;
  1640
+
  1641
+		/**
1634 1642
 		 * Number of bytes uploaded of the files total size.
1635 1643
 		 *
1636 1644
 		 * @property loaded
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.