拖拽本地文件夹到浏览器中,展示所有文件结构

基础内容看这里 HTML 拖放(Drag and Drop)功能的踩坑总结,本篇不再复述

1 效果说明

从本地拖拽文件(夹)到浏览器中,展示包含的所有文件。
在这里插入图片描述

2 分析

需要先获取拖拽的内容,得到一个文件(夹) list 。循环 list ,对于其中的每一项,如果是文件,那么直接获取;如果是文件夹,则需要递归读取内部文件。

在 drop 事件中可以得到 e.dataTransfer ,它有两个属性可以获得拖拽内容:

  • files :被拖动到浏览器窗口中的本地文件列表
  • items :拖动操作中被拖动项的 DataTransferItem 对象。(拖动项可能是文件,也可能是别的)

先说结论:不能用 e.dataTransfer.files ,必须用 e.dataTransfer.items

3 为什么不能用 e.dataTransfer.files ?

e.dataTransfer.files 是 FileList 对象,是伪数组对象(有 length 属性,可通过索引获取元素)。遍历得到 File 对象

dropArea.addEventListener("drop", e => {
	let files = e.dataTransfer.files; // FileList 对象
	for (let i = 0; i <= files.length - 1; i++) {
		let file = files.item(i); // File 对象
		console.log(file); 
	}
	e.preventDefault();
});

最终,我们拿到的是一个个 File 对象。但是,无法判断一个 File 对象是文件夹还是内容

File 对象属性如下:
拖拽本地文件夹到浏览器中,展示所有文件结构_第1张图片
不能用 type 来判断,因为不靠谱:

type 属性:浏览器不会实际读取文件的字节流,而是根据文件的扩展名来判断。而且,file.type 仅仅对常见文件类型可靠,不常见的文件扩展名会返回空字符串。

拖拽本地文件夹到浏览器中,展示所有文件结构_第2张图片
总结:用 e.dataTransfer.files 最终获取到的是 File 对象,无法判断一个 File 对象是文件还是文件夹,所以不能用!

4 用 e.dataTransfer.items

e.dataTransfer.items 是 DataTransferItemList 对象,是伪数组对象(有 length 属性,可通过索引获取元素)。遍历得到 DataTransferItem 对象。

dropArea.addEventListener("drop", e => {
	// DataTransferItemList 对象,是伪数组对象
	let items = e.dataTransfer.items;	
	for (let i = 0; i <= items.length - 1; i++) {
		//  DataTransferItem 对象 
		let item = items[i]; 
	}
	e.preventDefault();
});

每个对象,可能是文件,也可能是字符串,通过 kind 属性可以判断。

dropArea.addEventListener("drop", e => {
	let items = e.dataTransfer.items;
	for (let i = 0; i <= items.length - 1; i++) {
		let item = items[i];
		// 通过 kind 属性可以判断当前的 DataTransferItem 对象是文件还是字符串
		if (item.kind === "file") {
			 
		}
	}
	e.preventDefault();
});

使用 webkitGetAsEntry 方法:获取到一个 FileSystemFileEntry 对象或 FileSystemDirectoryEntry 对象。这两种都继承自 FileSystemEntry。

dropArea.addEventListener("drop", e => {
	let items = e.dataTransfer.items;
	for (let i = 0; i <= items.length - 1; i++) {
		let item = items[i];
		if (item.kind === "file") {
			// FileSystemFileEntry 或 FileSystemDirectoryEntry 对象
			let entry = item.webkitGetAsEntry();
			// 递归地获取entry下包含的所有File
			this.getFileFromEntryRecursively(entry);
		}
	}
	e.preventDefault();
});

getFileFromEntryRecursively 方法

使用 FileSystemEntry 对象的 isFile 属性,判断是文件还是文件夹。

getFileFromEntryRecursively(entry) {
      if (entry.isFile) {
      		// 文件
      } else {
      		// 文件夹        
	}
}

如果是文件的话,entry 的具体类型就是 FileSystemFileEntry,用 file 方法获得一个 File 对象:

FileSystemFileEntry.file(successCallback[, errorCallback]);

successCallback 中会传入 File 对象。注意 :这个 File 对象的相对路径是空(webkitRelativePath是空字符串),所以如果想要保留拖拽的层级结构,只能从 entry 中获取

getFileFromEntryRecursively(entry) {
      if (entry.isFile) {
      	// 文件
        entry.file(
        	// 
          file => {
          	// 想要保留拖拽的层级结构的话,只能从 entry 中获取
            this.addFileToList({ file, path: entry.fullPath });
          },
          e => { console.log(e); }
        );
      } else {
      	// 文件夹
      }
}

如果是文件夹的话,entry 的具体类型就是 FileSystemDirectoryEntry ,可以使用 createReader 方法获得一个 FileSystemDirectoryReader 对象。reader 的 readEntries 方法,获取这个 entry 下的子级 entries。

getFileFromEntryRecursively(entry) {
      if (entry.isFile) {
        entry.file(
          file => {
            this.addFileToList({ file, path: entry.fullPath });
          },
          e => { console.log(e); }
        );
      } else {
        let reader = entry.createReader();
        reader.readEntries(
          entries => {
            entries.forEach(entry => this.getFileFromEntryRecursively(entry));
          },
          e => { console.log(e); }
        );
      }
}

你可能感兴趣的:(经验思路总结,杂七杂八啥都学)