基础内容看这里 HTML 拖放(Drag and Drop)功能的踩坑总结,本篇不再复述
从本地拖拽文件(夹)到浏览器中,展示包含的所有文件。
需要先获取拖拽的内容,得到一个文件(夹) list 。循环 list ,对于其中的每一项,如果是文件,那么直接获取;如果是文件夹,则需要递归读取内部文件。
在 drop 事件中可以得到 e.dataTransfer
,它有两个属性可以获得拖拽内容:
先说结论:不能用 e.dataTransfer.files ,必须用 e.dataTransfer.items
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
是 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();
});
使用 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); }
);
}
}