A user account is required in order to edit this wiki, but we've had to disable public user registrations due to spam.

To request an account, ask an autoconfirmed user on Chat (such as one of these permanent autoconfirmed members).

DragAndDropEntries: Difference between revisions

From WHATWG Wiki
Jump to navigation Jump to search
Line 95: Line 95:


   var input = document.querySelector("input[type='file']");
   var input = document.querySelector("input[type='file']");
 
   input.addEventListener('change', function (e) {
   input.addEventListener('change', function (e) {
     e.stopPropagation();
     e.stopPropagation();
     e.preventDefault();
     e.preventDefault();
 
     var entries = e.target.webkitEntries;        // WebKit/chromium needs 'webkit' prefix (since Chrome 22)
     var entries = e.target.webkitEntries;        // WebKit/chromium needs 'webkit' prefix (since Chrome 22)
 
     for (var i = 0; i < entries.length; ++i) {  
     for (var i = 0; i < entries.length; ++i) {  
       if (entries[i].isDirectory) {
       if (entries[i].isDirectory) {

Revision as of 12:52, 20 June 2012

Proposing exposing dropped files/folders as {File,Directory}Entry defined in FileSystem API [1] for better folders/files drag-and-drop support (this proposal was originally proposed on whatwg mailing list).

[1] File API: Directories and System http://www.w3.org/TR/file-system-api/ [2] Original proposal email: http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2011-November/033814.html

Background

Many sites have 'upload your files' feature, like for your photo images. HTML5 allows you to do this via <input type="file" multiple> or drag-and-drop feature, but the current solution does not provide clean solution for cases with folders, files/folder mixed cases, or folders with subfolders cases.

Proposing API

Add a new field 'entries' to <input type=files> element [1] and a new accessor method 'getAsEntry()' to DataTransferItem object [2]. Populate the field with or return an 'Entry' object defined in FileSystem API upon file selection or drop events.

[1] http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#file-upload-state-(type=file) [2] http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#the-datatransferitem-interface

Since FileSystem API naturally supports tree-structured folder hierarchy, Entry object exposes handy fields like 'isFile' and 'isDirectory', and allows webapps to recursively walk over the nested entries in subfolders via ReadDirectory() method.

This approach allows webapps to directly interact with the local folder structure, and also allows them to control the enumerating part so that the apps can show nice progress meter if they want.

HTMLInputElement.entries

 interface HTMLInputElement : HTMLElement {
    // allows scripts to access the element's selected files as an array of Entry defined in FileSystem API.
    // On getting, if the IDL attribute applies, it must return a Entry array that represents the current selected files and/or folders.
    // If the IDL attribute does not apply, then it must instead return null.
    // Unless the multiple attribute is set, there must be no more than one file in the list of selected files.
    readonly attribute Entry[] entries;
 }

DataTransferItem.getAsEntry()

 interface DataTransferItem {
   // Like DataTransferItem.getAsFile(), getAsEntry() method runs following steps:
   // 1. If the DataTransferItem object is not in the read/write mode or the read-only mode, return null and abort these steps.
   // 2. If the drag data item kind is not File, then return null and abort these steps.
   // 3. Return a new Entry object representing the actual file or directory represented by the DataTransferItem object.
   Entry getAsEntry();
 };

Isolated FileSystem

The Entry exposed by this feature is created in an Isolated filesystem, which is read-only, isolated and transient. The filesystem only contains the dropped items and can only be accessed by the site where the drag-and-drop event has happened. Webapps cannot access any other files or folders outside the filesystem.

Restrictions

  • The Entry in isolated filesystem does not support toURL() or is not resolvable by resolveFileSystemURL(), since its filesystem is transient and has limited life time (while toURL URL's do not work as live references like Blob URLs). The dropped entries are only accessible while the app has live instance for the entries.
  • The Entry's fullPath does not (and should not) expose the file's full platform path as it could contain a sensitive information. Instead it MAY contain a relative path to the top directory that the user dropped.

Example

Assume a user has following files and folders in his/her 'Photos' folder:

   /Users/username/Photos/trip/1.jpg
   /Users/username/Photos/trip/2.jpg
   /Users/username/Photos/trip/3.jpg
   /Users/username/Photos/halloween/2011/a.jpg
   /Users/username/Photos/halloween/2011/b.jpg
   /Users/username/Photos/halloween/2012/plan.jpg
   /Users/username/Photos/tokyo/1.jpg
   /Users/username/Photos/tokyo/2.jpg

When the user drag-and-drops or selects the two folders, trip, halloween and one photo file tokyo/1.jpg, the webapp will get following entries via 'entries' field of the input element or via DataTransferItem.getAsEntry():

   DirectoryEntry for 'trip',
   DirectoryEntry for 'halloween'
   FileEntry for '1.jpg'

Via the new 'entries' field the app can access children files or subfolders in the selected entries, and can properly organize and process pictures using the local folder structure. Even if the dropped directory is on a slow removable media, or it contains tons of subdirectories and files, the app can recursively traverse each of them while showing a nice progress information, or can show an interactive file tree which lets the user freely expand or select some of the contents.

We can think of similar interesting usage scenarios like local-cloud sync app or bulk 'importer', e.g. importing local source directory to cloud IDE etc.

Example Code (Drag and Drop)

  var droptarget = document.getElementById('droptarget');
 
  droptarget.addEventListener('dop', function (e) {
    e.stopPropagation();
    e.preventDefault();

    var items = e.dataTransfer.items;
    for (var i = 0; i < items.length; ++i) { 
      if (items[i].kind != "file")
         continue;
 
      var entry = items[i].webkitGetAsEntry();    // WebKit/chromium needs 'webkit' prefix (since Chrome 21)
  
     // import the dropped files/folders into temporary filesystem
     entry.copyTo(temporaryFs.root, entry.name, successCallback, errorCallback);
  });

Example Code (Input)

 <input type="file" multiple />
  var input = document.querySelector("input[type='file']");
 
  input.addEventListener('change', function (e) {
    e.stopPropagation();
    e.preventDefault();
 
    var entries = e.target.webkitEntries;        // WebKit/chromium needs 'webkit' prefix (since Chrome 22)
 
    for (var i = 0; i < entries.length; ++i) { 
      if (entries[i].isDirectory) {
        traverseDirectoryTree(entry);
      } else {
        showFileEntry(entries[i]);
      }
  });