介绍

项目中有需要实现拖拽文件夹上传功能,虽然可以通过设置input 的webkitdirectory属性实现。但是这种方式的兼容性却不好。如图:
webkitdirectory兼容性
webkitdirectory兼容性
博主找遍全网都没有找到合适的解决方案,直到在网上找到一些例子
but这个例子有个问题:就是多个子文件夹时,路径数据是错误的。
由此本文修改后的代码,请以我为准。

思路

  • 获取文件夹中所有文件
  • 上传文件。(文件中需要包含对应文件的相对路径!!!)
  • 根据文件的路径需要后端在服务器创建对应文件夹,然后放置对应文件。(将文件存放至对应的文件夹)

功能

  1. 拖拽文件夹上传,主要难点是在获取文件夹各子文件的数据和路径。
  2. 支持多个文件,多个文件夹,多级文件夹上传。
  3. 兼容性: Chrome 25+

环境

  • 需要在服务下运行!!!

  • js 和原生DOM 和算法实现,除文中提到的polyfill.js之外不需要其他

步骤

引入polyfill.js

upload_polyfill.js下载

代码如下。(不需要vue环境,这里只是习惯用vue)

<template>
  <div>
    <div id="dropDiv">请将拖拽文件/文件夹至此</div>
    <h3>
      文件列表,更多请见console
    </h3>
    <div id="outPut"></div>
  </div>
</template>

<script>
import "./polyfill.js";
export default {
  name: "directory-upload-example",
  data() {
    return {

    };
  },
  mounted() {
    let dz = document.getElementById("dropDiv");
    dz.ondragover = function (ev) {
      //阻止浏览器默认打开文件夹的操作
      ev.preventDefault();
      //拖入文件后边框颜色变蓝
      this.style.borderColor = "#6ba1d0b3";
      this.style.borderWidth = "2px";
      this.style.borderStyle = "dashed";
      this.style.backgroundColor = "rgba(133, 192, 241, 0.3)";
    };
    dz.ondragleave = function () {
      //文件脱出热区后恢复边框颜色

      this.style.borderColor = "#cccccc";
      this.style.borderWidth = "1px";
      this.style.borderStyle = "solid";
      this.style.backgroundColor = "#fff";
    };
    dz.addEventListener("drop", function (e) {
      //拖动操作后的热区样式
      dz.style.borderColor = "#cccccc";
      dz.style.borderWidth = "1px";
      dz.style.borderStyle = "solid";
      dz.style.backgroundColor = "#fff";
      e.stopPropagation();
      e.preventDefault();

      let okFiles = [];   //递归文件夹后 保存所有file的Array
      let step = 0;       //当前遍历次数
      let folderNum = 1;  //需要遍历的次数  有一个子文件夹就需要遍历一次
      const iterateFilesAndDirs = async function (filesAndDirs, path) {  // 遍历目录的文件夹和文件
        const folderArr = filesAndDirs.filter((item) => {  //当前目录的子文件夹
          return item._items && item._items.isDirectory;
        });

        folderNum += folderArr.length;  //当前目录下还需遍历多少次  有多少个子文件夹就需要遍历几次
        step += 1;  //遍历次数
        for (let i = 0; i < filesAndDirs.length; i++) {
          if (typeof filesAndDirs[i].getFilesAndDirectories === "function") {//文件夹 遍历
            

            let dpath = filesAndDirs[i].path;
            
             // 递归遍历各级目录
            filesAndDirs[i]
              .getFilesAndDirectories()
              .then(function (subFilesAndDirs) {
                // 遍历子目录的文件夹和文件

                iterateFilesAndDirs(subFilesAndDirs, dpath);
              });
          } else {
            //如为文件则存储在okFiles 里面
            let file = filesAndDirs[i];
            file.fullPath = path; //组装该文件的相对路径
            okFiles.push(file);
          }
        }
        if (step >= folderNum) { //计算是否遍历完毕  即 当前共遍历了多少次 == 总共需要遍历的次数(文件夹数量) 则遍历完毕
          //确定是最后一次遍历后执行上传操作
          console.log(okFiles); //得到此次上传的所有文件 不包含空文件夹

          //上传okFiles 的文件
           let formData = new FormData();
          
          let fileStr = "";
          for(let file of okFiles){
            formData.append("file", file);
            fileStr+=`<span>名称:${file.name}   路径:${file.fullPath}</span><br/>`
          }
          document.getElementById("outPut").innerHTML=fileStr;
          // //上传文件
          // this.$axios
          // .post("url", formData)
          // .then((res) => {
          //   /*
          //   *
          //   * 
          //   */
          // })
          // .catch((error) => {
          //   /*
          //   *
          //   * 
          //   */
          // });


        }
      };

      // 遍历选定的文件夹 // 支持文件 文件夹  多个文件夹

      if ("getFilesAndDirectories" in e.dataTransfer) {
        e.dataTransfer.getFilesAndDirectories().then(function (filesAndDirs) {
          iterateFilesAndDirs(filesAndDirs, "/");
        });
      }
    });
  },
};
</script>

<style  scoped>
#dropDiv {
  width: 100%;
  border: 1px solid #cccccc;
  margin-top: 20px;
  padding: 80px 20px;
  text-align: center;
  color: #cccccc;
}
</style>

效果图

  • 文件夹内容
    demo1
    demo2
  • 上传
    上传效果
  • 效果
    success
Logo

前往低代码交流专区

更多推荐