Drag-and-drop file upload in Google Chrome/Chromium and Safari?

JavascriptAjaxHtml

Javascript Problem Overview


Drag-and-drop file uploading can be done in Firefox 3.6.

A Google search for html5 drag-and-drop file uploading -gmail gives things like:

All of these guides use FileReader (or the Firefox 3.6's deprecated getAsBinary, which no other browser supports, either).

However, Google recently released an update for Gmail that allowed drag-and-drop file uploading in Chromium as well as Firefox, and Chromium does not have FileReader. I'm using the latest Chromium nightly, and it can drag-drop upload files, while not supporting FileReader.

I've seen someone mention that drag-drop uploading can be possible by dragging onto an <input type="file" />, but that can only support one file at a time, while Gmail's uploader can handle multiple files being dragged onto it, so that's clearly not what they're doing.

So the question is, how do they do it? How do you support Chromium for HTML5 file uploading? In addition, can you support Safari?

Javascript Solutions


Solution 1 - Javascript

WARNING: This is compatibility code for very old versions of Safari and Chrome. Modern browsers all support the FileReader API; here's one tutorial: https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications

This code is now only useful if for some reason you have a need to support Safari 5 and older, or Chrome 6 and older.


One possibility is to use the method used in SwellJS:

Use <input type="file" multiple="multiple" /> like so:

<form method="post" enctype="multipart/form-data" id="uploadform">
  <input type="file" name="dragupload[]" multiple="multiple"
  onchange="if (this.value) document.getElementById('uploadform').submit();" />
</form>

The input element can be styled to have opacity: 0 and positioned (absolutely) over an element that accepts uploads. The entire form can be placed inside an iframe for "pseudo-Ajax" like behavior. And the upload element can be a layer hidden until something is dragged over it.

Such an iframe would look like:

<script>
<!--
  var entered = 0;
-->
</script>
<body ondragenter="entered++;document.getElementById('uploadelement').style.display='block'" ondragleave="entered--;if (!entered) document.getElementById('uploadelement').style.display='none'">
  <form method="post" enctype="multipart/form-data" id="uploadform">
    Things can be dragged and dropped here!
    <input type="file" id="uploadelement" name="dragupload[]" multiple="multiple" onchange="if (this.value) { document.getElementById('uploadform').submit(); }" style="display:none;position:absolute;top:0;left:0;right:0;bottom:0;opacity:0;" />
  </form>
</body>

This should only be done when Safari or Chrome is detected (since other browsers don't support drag-and-drop onto <input type="file" /> elements), and can be used in combination with the HTML5 drop event for Firefox 3.6+.

I can't tell if this is the method Gmail uses, but it certainly works about as well.

Solution 2 - Javascript

You may be interested on something more technology- and browser-compliant.

Seems to me that Plupload does it well, supporting the following features:

  • Chunking
  • Drag/Drop
  • PNG Resize
  • JPEG Resize
  • Type filtering
  • Stream upload
  • Multipart upload
  • File size restriction
  • Upload progress

for most of the following technologies:

  • Flash
  • Gears
  • HTML 5
  • Silverlight
  • BrowserPlus

And yes, since 2010.05.27, it supports drag/drop for HTML5 running on Chrome beta.

Solution 3 - Javascript

I've got something working in Chrome after much, much, much detective work. This only works on Chrome. On Safari, it freezes. On Firefox, it won't let me drop the file. IE opens the dropped file instead. Even in Chrome, the drag and drop only works once, for some reason, after which you have to refresh the page. (A possible reason for this is that something is wrong with the event handlers.)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<script type="text/javascript">
			window.onload = function () {
				var div = document.getElementById('div');
				div.ondragenter = div.ondragover = function (e) {
					e.preventDefault();
					e.dataTransfer.dropEffect = 'copy';
					return false;
				}
				div.ondrop = function (e) {
					for (var i = 0; i < e.dataTransfer.files.length; i++) { // e.dataTransfer is a DataTransfer object (https://developer.mozilla.org/En/DragDrop/DataTransfer), e.dataTransfer.files is a FileList object (https://developer.mozilla.org/en/DOM/FileList)
						var file = e.dataTransfer.files[i]; // file is a File object (https://developer.mozilla.org/en/DOM/File)
						
						var xhr = new XMLHttpRequest;
						xhr.open('post', 'handler.php', true);
						xhr.onreadystatechange = function () {
							if (this.readyState != 4)
								return;
							document.body.innerHTML += '<pre>' + this.responseText + '</pre>';
						}
						xhr.setRequestHeader('Content-Type', 'multipart/form-data');
						xhr.setRequestHeader('X-File-Name', file.fileName);
						xhr.setRequestHeader('X-File-Size', file.fileSize);
						xhr.send(file); // For some reason sending the actual File object in Chrome works?
					}
					
					e.preventDefault();
					return false;
				}
			}
		</script>
	</head>
	<body>
		<div id="div" style="width: 100%; height: 200px; border: 1px solid blue">Drop here</div>
	</body>
</html>

handler.php:

	// This is not a true file upload. Instead, it sends the raw data directly.
	echo htmlentities(file_get_contents('php://input'));

Solution 4 - Javascript

You wouldn't need to use an iframe to do pseudo ajax uploading. Chrome and Safari both support XHR2 uploads with progress events so you can do progress bars etc.

Solution 5 - Javascript

For our own application, we do drag and drop for FireFox only. We revert to the traditional iframe upload for others. In order to detect that drag and drop is supported, we run this code:

if (typeof(window.File) == 'object' && typeof(window.FileReader) == 'function' && typeof(window.FileList) == 'object') {
   // DnD is supported!
}

Hope this is helpful to some.

Solution 6 - Javascript

You can use html5uploader library: http://code.google.com/p/html5uploader/

It works with Firefox, Safari and Chrome.

Solution 7 - Javascript

The latest browser support file upload well. You could use:

xhr = new XMLHttpRequest();		
xhr.open('POST', targetPHP, true);
var formData = new FormData();
formData.append('upload',file);
xhr.send(formData);

You do not need to set boundary or any head,just like this it works fine. I tested this code in client:firefox 6.02 and in chrome 13. server:tomcat with "spring mvc"

Solution 8 - Javascript

you can use FormData to store the File, then upload it. e.g

function setUp(){
  var dropContainer = document.getElementById("container");
  dropContainer.addEventListener("drop",dropHandler,false);
  dropContainer.addEventListener("dragenter", function(event){event.stopPropagation();event.preventDefault();}, false);
  dropContainer.addEventListener("dragover", function(event){event.stopPropagation();event.preventDefault();}, false);
  dropContainer.addEventListener("drop", dropHandler, false);
  getResult()
}
function dropHandler(event){
  var files = event.dataTransfer.files;
  var count = files.length;
  form = new FormData();
  for(var i= 0;i<count;i++){
    form.append("file"+i, files[i]);
  }
  sendData();
}
function sendData(){
  var xhr = new XMLHttpRequest();  
  xhr.upload.addEventListener("progress", uploadProgress, false);  
  xhr.addEventListener("load", uploadComplete, false);
  xhr.addEventListener("error", uploadFailed, false);  
  xhr.open("POST", "/upload");
  xhr.send(form);
  var progressBar = document.getElementById('progressBar');
  progressBar.style.display = 'block';
  progressBar.style.width = '0px';
}

the demo is here(http://flexinnerp.appspot.com/) just enjoy it :)

Solution 9 - Javascript

Set multiple attribute like:

input type="file" name="file1" multiple="multiple" class="DropHere"

and use this CSS DropHere class:

.DropHere
{
	height: 100px;
    padding: 3px;
	border: 2px dashed #555;
	border-radius: 5px;
	cursor: default;
	background-image:url("data:image/svg+xml;utf8, <svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='100px' width='220px'><text x='55' y='75' font-size='20'>or drop files here</text></svg>");
	background-repeat: no-repeat;
}

The file field will now look like:

The file will now look like

If you use asp.net you might also like this article I wrote "Multiple file upload with progress bar and drag and drop": http://www.codeproject.com/Articles/818561/Multiple-file-upload-with-progress-bar-and-drag-an

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZarelView Question on Stackoverflow
Solution 1 - JavascriptZarelView Answer on Stackoverflow
Solution 2 - JavascriptArnaud LeymetView Answer on Stackoverflow
Solution 3 - JavascriptCasey ChuView Answer on Stackoverflow
Solution 4 - JavascriptRyan SeddonView Answer on Stackoverflow
Solution 5 - JavascriptMartinView Answer on Stackoverflow
Solution 6 - JavascriptWOjView Answer on Stackoverflow
Solution 7 - JavascriptsummerView Answer on Stackoverflow
Solution 8 - JavascripttaiView Answer on Stackoverflow
Solution 9 - JavascriptIgor KrupitskyView Answer on Stackoverflow