前言

文件拖动上传,对于个别应用场景十分有效,实现起来也并不难。参见
“浏览器图片预览 --http://blog.segmentfault.com/bornkiller/1190000000428572"。
现在更进一步,做到文件夹拖动。Filsesystem不是w3c标准,所以chrome浏览器支持度良好,其它浏览器大部分不支持。以下内容全基于chrome 36 版本测试成功。

参考资料

FileSystem API

API 被分为以下不同的主题:
读取和处理文件:File/Blob、FileList、FileReader
创建和写入:BlobBuilder、FileWriter
目录和文件系统访问:DirectoryReader、FileEntry/DirectoryEntry

启用文件存储系统

做文件处理,需要向浏览器申请空间。需要注意的是,此处申请的空间不可与硬盘上文件直接交互,在存储上,与cookie较为相似,存储在硬盘上,但不能与硬盘进行文件交换。

var requestFileSystem = window.requestFileSystem ?
               window.requestFileSystem:
               window.webkitRequestFileSystem;
// success 回调函数
var onInitFS = function(fs) {};
// error 回调函数
var onInitFE = function(fe) {};
requestFileSystem(window.TEMPORARY, 10*1024*1024, onInitFS, onInitError);

文件夹拖动

拦截默认行为,并阻止冒泡

文件夹拖动或者文件拖动,浏览器默认会直接打开显示,图片尤为明显,所以首先需要阻止默认行为。

window.addEventListener('load', function(evt) {
  document.addEventListener('drop', prevent);
  document.addEventListener('dragenter', prevent);
  document.addEventListener('dragleave', prevent);
  document.addEventListener('dragover', prevent);       
});

function prevent(e){
  e.stopPropagation();
  e.preventDefault();
}

获得FileEntry/DirectoryEntry

e.dataTransfer.items在firefox下为undefined,chrome下正常。

document.addEventListener('drop',entryResolve);
function entryResolve(e){
  var items = e.dataTransfer.items;
  // 多文件/文件夹拖动时,items.length即为拖入文件/文件夹数量
  var itemsCount = items.length;
  // 获取文件/文件夹Entry对象
  var entries = [];      
  for (var i=0; i<itemsCount; i++) {
     entries.push(items[i].webkitGetAsEntry());
  }
}

处理FileEntry/DirectoryEntry

//每个Entry对象具备isDirectory, isFile属性,判定目标为文件或是文件夹。具体接口可以自行查阅

entries.forEach(function(entry, key) {
   if(entry.isDirectory && !entry.isFile) {
       // entry is DirectoryEntry here
   }else if(!entry.isDirectory && entry.isFile) {
       // entry is FileEntry here
   }else{
      return false;
   }
})

文件夹遍历

  • 如果确定文件路径,可使用getFile()方法
  • 如果确定目录路径,可使用getDirectory()方法
  • 如果递归删除,可使用removeRecursively()方法
  • 如果不确定,只能采取目录遍历.由于目录遍历为异步操作,所以需要将目录内容处理回调函数作为传入参数。
  function readDir(directoryEntry, readDirCallback) {
     // 判定参数类型是否匹配
     if (!directoryEntry.isDirectory || !typeof value === 'function') {
        return false;
     }

     var fileEntriesContainer = [];
     // 创建目录遍历器 dirReader
     var dirReader = directoryEntry.createReader();

     // 遍历目录,由于无法一次性返回全部,所以需要递归调用,直到返回结果为空,执行回调函数。
     var readEntries = function() {
       dirReader.readEntries (function(results) {
         if (!results.length) {
           readDirCallback(fileEntriesContainer);
         } else {
           fileEntriesContainer = fileEntriesContainer.concat(toArray(results));
           readEntries();
         }
       }, errorHandler);
     };
     readEntries();
  }
  • 如果引入了Q模块,可以通过promise/defer方式来实现。
function readDirectoryEntry(directoryEntry) {
return  Q.Promise(function(resolve, reject) {
          if(directoryEntry.isDirectory) {
            resolve(true);
          } else {
            reject(false);
          }
        }).then(function() {
          return readDirectory(directoryEntry);
        })
};

// 返回值为promise,传递值为目录下所有的entry对象组成的数组。
function readDirectory(directoryEntry) {
var defer = Q.defer();

var fileEntriesContainer = [];
var dirReader = directoryEntry.createReader();

// Call the reader.readEntries() until no more results are returned.
var readEntries = function() {
  dirReader.readEntries(function(results) {
    if (!results.length) {
      defer.resolve(fileEntriesContainer);
    } else {
      fileEntriesContainer = fileEntriesContainer.concat(toArray(results));
      readEntries();
    }
  }, function(err) {
    defer.reject(err.message);
  });
};
readEntries();  

return defer.promise;
}

FileEntry 转换 File 及文件操作

// 通过file函数即可获得File 对象
// File 对象处理请自行参阅 FileReader
entry.file(function(file){
   var fileReader = new FileReader();
   fileReader.readAsText(file);
   fileReader.addEventListener('load', function(){
      console.log(this.result);
   })
})

交流

QQ : 491229492
注明交流即可


怀疑真爱的流浪者jason
923 声望62 粉丝

For every single second in life, I want to fight with the monster deep within my heart , and I want to win.........