views:

6854

answers:

8

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?

+8  A: 

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[]" 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.

Zarel
+3  A: 

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"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
    <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'));
Casey Hope
A: 

我怀疑是使用了Chrome自带的Gears来实现的

Desktop API ==> Blob API ==> HttpRequest API

又通过HttpAnalyzer看到Raw Stream是application/octet-stream类型的Body,而非multipart/form-data格式

renjie
No, I don't think you're right. Chrome doesn't support Gears in OS X, and Gmail's drag-drop uploading still works fine there.
Zarel
Gears is now deprecated
Romain Hippeau
+2  A: 

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.

thecssninja
...yes, that much is obvious. The problem here is that all the tutorials I've seen use `FileReader` or `getAsBinary()` (i.e. the functions Chrome doesn't support) to XHR2-upload a file. I doubt this is _required_, so what I'm basically doing here is asking how one would go about it if one wanted Chrome support.
Zarel
I've recently written another tutorial going into how Google does the drag and drop upload in Gmail - http://cssn.in/ja/027Using xhr2 upload doesn't need getAsBinary when you're dealing with a file input, which is what Gmail does for Chrome This article is a good read on how to use xhr2 upload to asynchronously upload multiple files in a file input http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html
thecssninja
+4  A: 

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.

Arnaud Leymet
I just tested it on Chrome 6 for Mac: It doesn't work.
Zarel
Oh? Did you try the "HTML 5 runtime" of this page? http://www.plupload.com/example_all_runtimes.php
Arnaud Leymet
Yes, I tried the HTML 5 runtime specifically.
Zarel
The HTML5 runtime version works for me with 6.0.472.25 dev on OS X. It also seems to work with the Firefox 4 betas.
James Snyder
A: 

i've got also some pb with chrome / plupload(with html5 runtime)

Jimmny.c
A: 

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.

Martin
Thanks - it is helpful :)
pedro_sland
A: 

Gmail uses flash to support multiple files drop. U can find out by disabling flash.

qevan