前言
首先说一下,小弟第一次写文章,如果有什么错误 还望小哥哥 小姐姐多多包涵。如果有什么缺陷还望大家指出来 让小弟多学习。
内容划分
上传文件夹一共分两部分来写 一方面怕太长了 大家看五分钟就不想看了 二是没有那么多时间写本文 所以分为两次 这两篇文章我只写上传文件夹重要的知识点内容 其他知识点就不细写了 我相信掘金肯定会有更优秀的文章
- 上传文件夹的前期基础知识准备
- 完成上传文件夹的功能的思路和遇到的问题
组件功能(主要满足公司的业务功能 文件夹里目前只带图片 如果有其他的文件 我会移除)
- 可以拖拽上传文件夹 文件夹下面可以嵌套文件夹
- 可以点击上传多图片
- 可以一起拖文件夹和图片
- 也有一些上传成功 失败 进度等等的回调
组件背景
公司业务需求 需要传很多图片而且有文件夹区分。所以要求做一个文件夹可以拖拽的组件。(因为人手不够加上公司定制化的东西多 所以我现在主要负责公司前端的组件开发 目前不写业务。这个也是我自己拦下的 虽然有时候比较难 但是能夯实很多基础知识 )
怎么写
之前只做过图片上传,但是没有做过文件夹上传。一时间不知道从何下手。怎么办 第一步就是看了一下某盘的上传操作。我试着拖动文件夹上传。整理了上传思路。
- 首先读取到文件夹的名称 包括文件夹下面的文件夹 组成树形结构
- 组成文件夹树形结构之后 遍历上传文件夹
- 在服务器上创建同名文件夹
- 再把文件夹的图片上传到同名文件夹下
整体思路就是这样。看着是不是很简单呢。。。对我来说 其实一点也不简单。 我写了一周才写完。。。不碰到难点的东西 都不知道自己弱。。
技术实现
首先基础拖拽drop等事件我就不说。掘金上面也有很多优秀的文章。我主要说一下文件夹名称的获取。 因为很多人发现拖拽通过 event.dataTransfer.files 可以读取文件夹名字 但是我们拿不到里面的文件啊!!!怎么办呢? 我们知道 dataTransfer.files 返回的是File对象。列表里面的每项是每个读取到的文件。里面包括name、type、size等属性。那么文件夹里面的文件怎么办呢。其实可以通过dataTransfer.items。它返回的是一个DataTransferItemList对象,表示在拖动操作中拖动的项目的对象。但是只有这个还不够。我们需要它的一个方法 webkitGetAsEntry() 来返回文件。这个方法是非标准的。这时候就判断是文件夹还是文件 通过 isDirectory 方法和 isFile 来判断,如果是文件夹目录。就需要递归得到该目录。 第一步是创建一个FileSystemDirectoryReader来处理目录内容。 通过createReder()方法来完成。 之后 readEntries()调用读取目录中的所有文件。 上面所有的内容我们都可以在MDN上找到 而且MDN上面也给了说明和代码示例。(非常感谢MDN)。接下来我贴一段代码示例
复制代码拖动文件夹到此处上传
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。