js获取拖拽文件夹中所有的文件,遍历拖拽的文件及文件夹中所有的文件,并展示出来:
一、效果说明
从本地拖拽文件(夹)到浏览器中,展示包含的所有文件。
二、分析
需要先获取拖拽的内容,得到一个文件(夹) list 。循环 list ,对于其中的每一项,如果是文件,那么直接获取;如果是文件夹,则需要递归读取内部文件。
在 drop 事件中可以得到 e.dataTransfer ,它有两个属性可以获得拖拽内容:
files :被拖动到浏览器窗口中的本地文件列表
items :拖动操作中被拖动项的 DataTransferItem 对象。(拖动项可能是文件,也可能是别的)
先说结论:不能用 e.dataTransfer.files ,必须用 e.dataTransfer.items
三、为什么不能用 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 对象属性如下:
不能用 type 来判断,因为不靠谱:
type 属性:浏览器不会实际读取文件的字节流,而是根据文件的扩展名来判断。而且,file.type 仅仅对常见文件类型可靠,不常见的文件扩展名会返回空字符串。
总结:用 e.dataTransfer.files 最终获取到的是 File 对象,无法判断一个 File 对象是文件还是文件夹,所以不能用!
四、用 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); } ); } }
本文为CSDN博主「呀呀夫斯基」的原创文章,原文链接:https://blog.csdn.net/tangran0526/article/details/104108551