拖拽文件夹上传 一(基于Vue的文件夹上传组件)

5,939 阅读5分钟

前言

首先说一下,小弟第一次写文章,如果有什么错误 还望小哥哥 小姐姐多多包涵。如果有什么缺陷还望大家指出来 让小弟多学习。

内容划分

上传文件夹一共分两部分来写 一方面怕太长了 大家看五分钟就不想看了 二是没有那么多时间写本文 所以分为两次 这两篇文章我只写上传文件夹重要的知识点内容 其他知识点就不细写了 我相信掘金肯定会有更优秀的文章

  • 上传文件夹的前期基础知识准备
  • 完成上传文件夹的功能的思路和遇到的问题

组件功能(主要满足公司的业务功能 文件夹里目前只带图片 如果有其他的文件 我会移除)

  • 可以拖拽上传文件夹 文件夹下面可以嵌套文件夹
  • 可以点击上传多图片
  • 可以一起拖文件夹和图片
  • 也有一些上传成功 失败 进度等等的回调

组件背景

公司业务需求 需要传很多图片而且有文件夹区分。所以要求做一个文件夹可以拖拽的组件。(因为人手不够加上公司定制化的东西多 所以我现在主要负责公司前端的组件开发 目前不写业务。这个也是我自己拦下的 虽然有时候比较难 但是能夯实很多基础知识 )

怎么写

之前只做过图片上传,但是没有做过文件夹上传。一时间不知道从何下手。怎么办 第一步就是看了一下某盘的上传操作。我试着拖动文件夹上传。整理了上传思路。

  • 首先读取到文件夹的名称 包括文件夹下面的文件夹 组成树形结构
  • 组成文件夹树形结构之后 遍历上传文件夹
  • 在服务器上创建同名文件夹
  • 再把文件夹的图片上传到同名文件夹下

整体思路就是这样。看着是不是很简单呢。。。对我来说 其实一点也不简单。 我写了一周才写完。。。不碰到难点的东西 都不知道自己弱。。

技术实现

首先基础拖拽drop等事件我就不说。掘金上面也有很多优秀的文章。我主要说一下文件夹名称的获取。 因为很多人发现拖拽通过 event.dataTransfer.files 可以读取文件夹名字 但是我们拿不到里面的文件啊!!!怎么办呢? 我们知道 dataTransfer.files 返回的是File对象。列表里面的每项是每个读取到的文件。里面包括name、type、size等属性。那么文件夹里面的文件怎么办呢。其实可以通过dataTransfer.items。它返回的是一个DataTransferItemList对象,表示在拖动操作中拖动的项目的对象。但是只有这个还不够。我们需要它的一个方法 webkitGetAsEntry() 来返回文件。这个方法是非标准的。这时候就判断是文件夹还是文件 通过 isDirectory 方法和 isFile 来判断,如果是文件夹目录。就需要递归得到该目录。 第一步是创建一个FileSystemDirectoryReader来处理目录内容。 通过createReder()方法来完成。 之后 readEntries()调用读取目录中的所有文件。 上面所有的内容我们都可以在MDN上找到 而且MDN上面也给了说明和代码示例。(非常感谢MDN)。接下来我贴一段代码示例


 <div id="app">
  <div class="upload upload-box" id="upload">
   拖动文件夹到此处上传
  </div>
 </div>
 

const upload = document.getElementById('upload');

upload.addEventListener('dragover', uploadFunc, false);
upload.addEventListener('drop', uploadFunc, false);

function uploadFunc(event) {
  event.preventDefault();
  
  let files = [];
  if (event.type === 'drop') {
    let fileList = event.dataTransfer.files;
    let len = fileList.length;
   
    for (let i = 0; i < len; i++) {
      files[i] = fileList[i];
    }
   
    if (files.length) {
      let items = event.dataTransfer.items;
      if (items && items.length && items[0].webkitGetAsEntry != null) {
        addFilesItems(items);
      }
    }
  }
}

  function addFilesItems(items) {
    return function() {
      var ret = [];
      for (var i = 0; i < items.length; i++) {
        var item = items[i];
        var entry;

        if (item.webkitGetAsEntry && (entry = item.webkitGetAsEntry())) {
          if (entry.isFile) {
            // 把文件对象放到结果数组中  这里的addFile方法要单独写 我没有写上 这个不是重点
            ret.push(addFile(item.getAsFiles()));
          } else if (entry.isDirectory) {
            ret.push(addFilesFormDirectory(entry, entry.name));
          }
        }
      }
    }();
 } 

// 读取文件夹下的文件
function addFilesFormDirectory(directory, path) {
  const dirReader = directory.createReader();

  const readEntries = function readEntries() {
    return dirReader.readEntries(function(entries) {
      entries.forEach(function(entry){
        if (entry.isFile) {
          // 如果是文件
          entry.file(function(file){
            file.fullPath = path + '/' + file.name;
            // 那么暴露出去的就是 '文件夹/q.jpg' 这种形式 
            return addFile(file);
          });
          return addFile(file);
        } else if (entry.isDirectory) {
          // 递归处理
          addFilesFormDirectory(entry, path + '/' + entry.name);
        }
      });
    });
  };
  return readEntries();
}

通过readEntries会读取到下面的内容

上面就是拖拽上传文件夹需要准备的知识点。

最后

其实文件夹上传只是在服务器创建一个名字。用一个字段告诉我这是文件夹。难点在于怎么先创建好文件夹 在把客户端这个文件夹下面的文件上传到创建好的文件夹下面。还有FileReader相关的知识点 大家可以去高程书里面的25章节里面查看。里面写的很详细。再配合上饿了么上传组件的源码就更完美。我相信如果你仔仔细细阅读完它的源码 并自己实现一个上传组件,我相信你能学到更多,反正我是看完它的源码写了一个上传组件。写完之后 感觉真香。。。。

总结

这些知识点都是我查阅很多资料整理的,而且告诉大家一个秘密。有一个dropzonejs的库。 我的项目就是基于它实现的。有很多完整的功能帮助我完成上传文件夹组件。因为有些功能我自己写会很费时间而且有的不一定会写 所以我就直接使用它的API 阅读了它的源码。你也可以去看看官方文档 最后谢谢dropzone

过几天我会更新下一篇 实现上传文件夹的一些思路和遇到问题。下一篇也会把这个项目开源到我的github。