Javascript 如何使用更改和删除事件在firefox和chrome/chrome上上载和列出目录

Javascript 如何使用更改和删除事件在firefox和chrome/chrome上上载和列出目录,javascript,file-upload,directory,fileapi,Javascript,File Upload,Directory,Fileapi,mozilla和webkit浏览器现在都允许目录上传。当在元素中选择目录或在元素中删除目录时,如何按照firefox和chrome/chrome中实际目录中出现的顺序列出所有目录和文件,并在所有上传的目录都被迭代后对文件执行任务?您可以在元素中设置webkitdirectory和allowdirs属性;将change,drop事件附加到元素;在mozilla上使用.getFilesAndDirectories(),.createReader(),.readEntries()在webkit,Arr

mozilla和webkit浏览器现在都允许目录上传。当在
元素中选择目录或在元素中删除目录时,如何按照firefox和chrome/chrome中实际目录中出现的顺序列出所有目录和文件,并在所有上传的目录都被迭代后对文件执行任务?

您可以在
元素中设置
webkitdirectory
allowdirs
属性;将
change
drop
事件附加到
元素;在mozilla上使用
.getFilesAndDirectories()
.createReader()
.readEntries()
webkit
Array.prototype.reduce()
Promise
,递归

注意:在firefox上,drop事件不会将所选内容列为
目录
,而是具有
大小的
文件
对象,因此在firefox上drop目录不会提供已删除文件夹的表示,即使使用了
事件.dataTransfer.getFilesAndDirectories()
。当设置了
allowdirs
属性时,firefox还提供了两个输入元素;第一个元素允许单文件上传,第二个元素允许目录上传。chrome/chrome提供单个
元素,其中只能选择单个或多个目录,不能选择单个文件

在同时包含文件和目录的目录中,首先读取目录

<!DOCTYPE html>
<html>

<head>
  <style type="text/css">
    input[type="file"] {
      width: 98%;
      height: 180px;
    }

    label[for="file"] {
      width: 98%;
      height: 180px;
    }

    .area {
      display: block;
      border: 5px dotted #ccc;
      text-align: center;
    }

    .area:after {
      display: block;
      border: none;
      white-space: pre;
      content: "Drop your files or folders here!\aOr click to select files folders";
      position: relative;
      left: 0%;
      top: -75px;
      text-align: center;
    }

    .drag {
      border: 5px dotted green;
      background-color: yellow;
    }

    #result ul {
      list-style: none;
      margin-top: 20px;
    }

    #result ul li {
      border-bottom: 1px solid #ccc;
      margin-bottom: 10px;
    }

    #result li span {
      font-weight: bold;
      color: navy;
    }
  </style>
</head>


<body>
  <label id="dropArea" class="area">
    <input id="file" type="file" directory allowdirs webkitdirectory/>
  </label>
  <output id="result">
    <ul></ul>
  </output>
  <script>
    var dropArea = document.getElementById("dropArea");
    var output = document.getElementById("result");
    var ul = output.querySelector("ul");

    function dragHandler(event) {
      event.stopPropagation();
      event.preventDefault();
      dropArea.className = "area drag";
    }

    function filesDroped(event) {
      var webkitResult = [];
      var mozResult = [];
      var files;
      console.log(event);
      event.stopPropagation();
      event.preventDefault();
      dropArea.className = "area";

      // do mozilla stuff
      // TODO adjust, call `listDirectory()`, `listFile()`
      function mozReadDirectories(entries, path) {
        console.log("dir", entries, path);
        return [].reduce.call(entries, function(promise, entry) {
            return promise.then(function() {
              return Promise.resolve(entry.getFilesAndDirectories() || entry)
                .then(function(dir) {
                  return dir
                })
            })
          }, Promise.resolve())
          .then(function(items) {
            var dir = items.filter(function(folder) {
              return folder instanceof Directory
            });
            var files = items.filter(function(file) {
              return file instanceof File
            });
            if (files.length) {
              // console.log("files:", files, path);
              files.forEach(function(file) {
                console.log(file)
              });
              mozResult = mozResult.concat.apply(mozResult, files);
            }
            if (dir.length) {
              // console.log(dir, dir[0] instanceof Directory);
              return mozReadDirectories(dir, dir[0].path || path);

            } else {
              if (!dir.length) {
                return Promise.resolve(mozResult).then(function(complete) {
                  return complete
                })
              }
            }

          })

      };

      function handleEntries(entry) {
        let file = "webkitGetAsEntry" in entry ? entry.webkitGetAsEntry() : entry
        return Promise.resolve(file);
      }

      function handleFile(entry) {
        return new Promise(function(resolve) {
          if (entry.isFile) {
            entry.file(function(file) {
              listFile(file, entry.fullPath).then(resolve)
            })
          } else if (entry.isDirectory) {
            var reader = entry.createReader();
            reader.readEntries(webkitReadDirectories.bind(null, entry, handleFile, resolve))
          } else {
            var entries = [entry];
            return entries.reduce(function(promise, file) {
                return promise.then(function() {
                  return listDirectory(file)
                })
              }, Promise.resolve())
              .then(function() {
                return Promise.all(entries.map(function(file) {
                  return listFile(file)
                })).then(resolve)
              })
          }
        })

        function webkitReadDirectories(entry, callback, resolve, entries) {
          console.log(entries);
          return listDirectory(entry).then(function(currentDirectory) {
            console.log(`iterating ${currentDirectory.name} directory`, entry);
            return entries.reduce(function(promise, directory) {
              return promise.then(function() {
                return callback(directory)
              });
            }, Promise.resolve())
          }).then(resolve);
        }

      }
      // TODO: handle mozilla directories, additional directories being selected dropped and listed without
      // creating nested list at `html` where different directory selected or dropped
      function listDirectory(entry) {
        console.log(entry);
        var path = (entry.fullPath || entry.webkitRelativePath.slice(0, entry.webkitRelativePath.lastIndexOf("/")));
        var cname = path.split("/").filter(Boolean).join("-");
        console.log("cname", cname)
        if (!document.getElementsByClassName(cname).length) {
          var directoryInfo = `<li><ul class=${cname}>
                      <li>
                      <span>
                        Directory Name: ${entry.name}<br>
                        Path: ${path}
                        <hr>
                      </span>
                      </li></ul></li>`;
          var curr = document.getElementsByTagName("ul");
          var _ul = curr[curr.length - 1];
          var _li = _ul.querySelectorAll("li");
          if (!document.querySelector("[class*=" + cname + "]")) {
            if (_li.length) {
              _li[_li.length - 1].innerHTML += `${directoryInfo}`;
            } else {
              _ul.innerHTML += `${directoryInfo}`
            }
          } else {
            ul.innerHTML += `${directoryInfo}`
          }
        }
        return Promise.resolve(entry);
      }
       // TODO: handle mozilla files
      function listFile(file, path) {
        path = path || file.webkitRelativePath || "/" + file.name;
        var filesInfo = `<li>
                        Name: ${file.name}</br> 
                        Size: ${file.size} bytes</br> 
                        Type: ${file.type}</br> 
                        Modified Date: ${file.lastModifiedDate}<br>
                        Full Path: ${path}
                      </li>`;

        var currentPath = path.split("/").filter(Boolean);
        currentPath.pop();
        var appended = false;
        var curr = document.getElementsByClassName(`${currentPath.join("-")}`);
        if (curr.length) {
          for (li of curr[curr.length - 1].querySelectorAll("li")) {
            if (li.innerHTML.indexOf(path.slice(0, path.lastIndexOf("/"))) > -1) {
              li.querySelector("span").insertAdjacentHTML("afterend", `${filesInfo}`);
              appended = true;
              break;
            }

          }
          if (!appended) {
            curr[curr.length - 1].innerHTML += `${filesInfo}`;
          }
        }
        console.log(`reading ${file.name}, size: ${file.size}, path:${path}`);
        webkitResult.push(file);
        return Promise.resolve(webkitResult)
      };

      function processFiles(files) {
        Promise.all([].map.call(files, function(file, index) {
            return handleEntries(file, index).then(handleFile)
          }))
          .then(function() {
            console.log("complete", webkitResult)
          })
          .catch(function(err) {
            alert(err.message);
          })
      }

      if ("getFilesAndDirectories" in event.target) {
        return (event.type === "drop" ? event.dataTransfer : event.target).getFilesAndDirectories()
          .then(function(dir) {
            if (dir[0] instanceof Directory) {
              console.log(dir)
              return mozReadDirectories(dir, dir[0].path || path)
                .then(function(complete) {
                  console.log("complete:", complete);
                  event.target.value = null;
                });
            } else {
              if (dir[0] instanceof File && dir[0].size > 0) {
              return Promise.resolve(dir)
                    .then(function(complete) {
                      console.log("complete:", complete);
                    })
              } else {
                if (dir[0].size == 0) {
                  throw new Error("could not process '" + dir[0].name + "' directory"
                                  + " at drop event at firefox, upload folders at 'Choose folder...' input");
                }
              }
            }
          }).catch(function(err) {
            alert(err)
          })
      }

      // do webkit stuff
      if (event.type === "drop" && event.target.webkitdirectory) {
        files = event.dataTransfer.items || event.dataTransfer.files;
      } else if (event.type === "change") {
        files = event.target.files;
      }

      if (files) {
        processFiles(files)
      }

    }
    dropArea.addEventListener("dragover", dragHandler);
    dropArea.addEventListener("change", filesDroped);
    dropArea.addEventListener("drop", filesDroped);
  </script>
</body>

</html>

输入[type=“file”]{
宽度:98%;
高度:180像素;
}
标签[for=“file”]{
宽度:98%;
高度:180像素;
}
.区域{
显示:块;
边框:5px点#ccc;
文本对齐:居中;
}
.地区:之后{
显示:块;
边界:无;
空白:预处理;
内容:“将文件或文件夹放到此处!\a或单击以选择文件或文件夹”;
位置:相对位置;
左:0%;
顶部:-75px;
文本对齐:居中;
}
.拖{
边框:5px点绿色;
背景颜色:黄色;
}
#结果ul{
列表样式:无;
边缘顶部:20px;
}
#结果ulli{
边框底部:1px实心#ccc;
边缘底部:10px;
}
#结果李斯潘{
字体大小:粗体;
颜色:海军蓝;
}
    var dropArea=document.getElementById(“dropArea”); var输出=document.getElementById(“结果”); var ul=输出查询选择器(“ul”); 函数dragHandler(事件){ event.stopPropagation(); event.preventDefault(); dropArea.className=“区域拖动”; } 函数文件已删除(事件){ var webkitResult=[]; var mozResult=[]; var文件; console.log(事件); event.stopPropagation(); event.preventDefault(); dropArea.className=“区域”; //你喜欢mozilla吗 //要进行调整,请调用`listDirectory()`,`listFile()` 函数目录(条目、路径){ log(“dir”、条目、路径); return[].reduce.call(条目,函数(承诺,条目){ return promise.then(函数(){ 返回Promise.resolve(entry.getfilesandtirectories()| | entry) .then(功能(目录){ 返回目录 }) }) },Promise.resolve()) .然后(功能(项目){ var dir=items.filter(函数(文件夹){ 返回目录的文件夹实例 }); var files=items.filter(函数(文件){ 返回文件实例 }); if(files.length){ //log(“文件:”,文件,路径); forEach(函数(文件){ console.log(文件) }); mozResult=mozResult.concat.apply(mozResult,文件); } if(直达长度){ //log(dir,dir[0]instanceof Directory); 返回MozReadDirectory(dir,dir[0].path | | path); }否则{ 如果(!目录长度){ 返回Promise.resolve(mozResult).then(函数(complete){ 返回完成 }) } } }) }; 功能手柄入口(入口){ 让file=“webkitGetAsEntry”进入entry?entry.webkitGetAsEntry():entry 返回承诺。解决(文件); } 函数句柄文件(条目){ 返回新承诺(函数(解析){ if(entry.isFile){ entry.file(函数(文件){ listFile(文件,entry.fullPath)。然后(解析) }) }else if(entry.isDirectory){ var reader=entry.createReader(); reader.readEntries(webkitReadDirectories.bind(null、entry、handleFile、resolve)) }否则{ var条目=[entry]; 返回条目.reduce(函数(承诺,文件){ return promise.then(函数(){ 返回列表目录(文件) }) },Promise.resolve()) .然后(函数(){ 返回Promise.all(entries.map)(函数(文件){ 返回列表文件(文件) })).然后(解决) }) } }) 函数WebKitReadDirectory(条目、回调、解析、条目){ 控制台日志(条目); 返回listDirectory(条目)。然后返回函数(currentDirectory){ log(`iterating${currentDirectory.name}目录',条目); 返回条目.reduce(函数(承诺,目录){ return promise.then(函数(){ 返回回调(目录) }); },Promise.resolve()) }).然后(决定); } } //TODO:处理mozilla目录,