点击选取文件夹
我们知道,选取文件可以通过以下方式:
| 1
 | <input type="file" multiple/>
 | 
其实,想要选取文件夹只需要加上 webkitdirectory 属性:
| 1
 | <input id="inputFile" type="file" multiple webkitdirectory/>
 | 
现在可以选中文件夹了,那么怎么获取文件夹里的文件呢?方法和获取文件一样,只不过变成了包含多个文件的列表。代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | window.onload = function() {const input = document.getElementById("inputFile");
 input.onchange = event => {
 const fileList = event.currentTarget.files;
 for (let file of fileList) {
 console.log(file.name, file.webkitRelativePath, file.size);
 }
 }
 }
 
 | 
拖拽文件夹(文件)
思路介绍
拖拽上传文件夹主要通过 drop 事件实现。
| 1
 | box.addEventListener("drop", event => {});
 | 
这里我们主要用到 event.dataTransfer.items 属性,它是一个 DataTransferItemList 对象,若选中 n 个文件夹或文件,则它的 lenght 为 n。
DataTransferItemList 对象有一个 webkitGetAsEntry() 方法,该方法返回一个 FileSystemEntry 对象(包括 FileEntry 和 DirectoryEntry)。
每个 FileSystemEntry 对象都有 isFile 和 isDirectory 两个属性,便于判断是文件还是文件夹。它还有 fullPath 属性表示从选取的文件夹开始的路径。
如果是文件夹(DirectoryEntry),它有一个 createReader() 方法,可以创建一个 reader,reader 通过 readEntries() 方法获取文件夹中的内容,如下所示:
| 12
 
 | reader.readEntries(results => {});
 
 | 
得到一个 FileSystemEntry 类型的数组(results)。
注意: 该方法是异步执行的。而且,在文件夹中的文件
数目小于 100 时,可以读取到文件夹中的每个文件,但当文件数量大于 100 时,它最多只能读取到 100 个文件。因此,建议用递归获取完整的 results。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | let fileEntryList = [];
 const getFileEntries = function() {
 dirReader.readEntries(results => {
 if (results.length) {
 fileEntryList = fileEntryList.concat(results);
 getFileEntries();
 }
 });
 }
 
 getFileEntries();
 
 | 
如果是文件(FileEntry),它有 file 方法,可以获取文件的信息。
| 1
 | fileEntry.file(fileData =-> {});
 | 
fileData 中包含属的属性有:
- name:文件名
- size:文件大小
注意:该方法也是异步执行的。
代码
html:
| 12
 
 | <div id="box">拖拽上传(可选多个文件夹)</div><ul id="list"></ul>
 
 | 
js:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 
 | const box = document.getElementById("box");
 box.addEventListener("dragover", e => {
 
 e.preventDefault();
 
 e.stopPropagation();
 });
 
 box.addEventListener("drop", e => {
 e.preventDefault();
 e.stopPropagation();
 
 dropedFileReader(e).then(fileList => {
 fileList.forEach(file => {
 const dom_ul = document.getElementById("list");
 const dom_li = document.createElement("li");
 dom_li.innerHTML = `name: ${file.name},path: ${file.fullPath},size: ${file.size}`;
 dom_ul.appendChild(dom_li);
 });
 });
 });
 
 async function readRoll(fileEntryList) {
 for (let idx in fileEntryList) {
 const fileEntry = fileEntryList[idx];
 if (fileEntry.isDirectory) {
 
 let result = await readDirectory(fileEntry);
 fileEntryList[idx] = result;
 await readRoll(result);
 }
 }
 }
 
 function readDirectory(fileEntry) {
 return new Promise((resolve, reject) => {
 
 const dirReader = fileEntry.createReader();
 let fileEntryList = [];
 
 const getFileEntries = function() {
 
 dirReader.readEntries(results => {
 if (results.length) {
 fileEntryList = fileEntryList.concat(results);
 getFileEntries();
 } else {
 resolve(fileEntryList);
 }
 });
 }
 
 getFileEntries();
 });
 }
 
 function readFileEntry(fileEntry) {
 return new Promise(resolve => {
 let fileData;
 
 fileEntry.file(data => {
 fileData = data;
 fileData.fullPath = fileEntry.fullPath;
 resolve(fileData);
 });
 });
 }
 
 async function dropedFileReader(event) {
 let fileEntryList = [];
 
 const dataTransferItemList = event.dataTransfer.items;
 for (let item of dataTransferItemList) {
 fileEntryList.push(item.webkitGetAsEntry());
 }
 
 await readRoll(fileEntryList);
 
 fileEntryList = fileEntryList.flat(Infinity);
 
 let fileList = [];
 for (let fileEntry of fileEntryList) {
 const fileData = await readFileEntry(fileEntry);
 fileList.push(fileData);
 }
 
 return fileList;
 }
 
 |