一、实现效果

左侧区域支持选择一个系统中的文件夹,或者将文件夹拖拽到这个区域进行上传,右侧区域可以将文件夹的结构展示为树形结构。

二、代码实现

由于需要使用树形插件zTree,这个插件是依赖于jquery的,所以在项目中我们需要引入:

1、jquery

2、zTree:官网链接API Document [zTree -- jQuery tree plug-ins.]

项目结构很简单,一个zTree源码的文件夹,一个index.html文件

下载完zTree源码之后,解压放到index.html平级的位置

3、在index.html的head标签中引入相关依赖

<script type="text/javascript" src="zTree_v3-master/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="zTree_v3-master/js/jquery.ztree.core.js"></script>
<script type="text/javascript" src="zTree_v3-master/js/jquery.ztree.excheck.js"></script>
<script type="text/javascript" src="zTree_v3-master/js/jquery.ztree.exedit.js"></script>
<link rel="stylesheet" href="zTree_v3-master/css/zTreeStyle/zTreeStyle.css" type="text/css">

4、搭建html结构,左边放置拖拽框和input输入框,右边放置树结构

<style>li {list-style: none;}a {cursor: pointer;}.icon-download {font-size: 30px;cursor: pointer;}#drop {width: 500px;height: 500px;border: 1px solid black;float: left;}#folder_container {float: left;}</style>
    <!--文件夹下所有文件的信息 --><div id="drop"><input type="file" id="file_input" name="folder" webkitdirectory /><div style="text-indent: 10px"> 将文件夹拖到这里进行上传</div></div><!-- 树 --><ul id="folder_container"><ul id="fileTree" class="ztree"></ul></ul>

5、input框上传文件夹实现

input框可以增加一个属性webkitdirectory实现上传文件夹,不过这个功能过低版本的浏览器是不兼容的。使用onchange事件监听这个input框的输入事件,就可以在上传文件夹完毕通过event.target.files获取文件夹中的所有文件。

获取的是一个fileList,是一个类数组对象,每一个元素是一个file信息,其中包含文件更新时间、文件名、大小、类型、相对路径。

我们需要解析文件的相对路径,从而获取真正的文件夹结构。

首先需要使用Array.from()方法将类数组对象转换为数组,这样就可以使用数组的迭代方法。

具体的解析过程放在createTree方法中

<script>let files = []const fileInput = $('#file_input');fileInput.bind('change', function (e) {files = Array.from(e.target.files)createTree();})function createTree() {}
</script>

我们对文件的相对路径进行解析,最终是要放在zTree树上。zTree的节点之间是通过parentId这个属性来确定层级关系的。如果一个节点的parentId为null,证明这个节点就是根节点;一个节点的parentId等于其父节点的Id。所以在解析相对路径的时候,首先需要获取层数,知道这个文件的结构总共有几层。通过split(‘/’)就可以获取从外到内具体的名字。

(1)在设置根节点之前,要先初始化一棵树

let zNodes = [];
function initNodes() {zNodes = [];$.fn.zTree.init($("#fileTree"), setting, zNodes);
}
const setting = {}function createTree() {initNodes();
}

(2)确认根节点,也就是根目录的名字。每一个文件的头头都带着根目录的名字。粘贴一下完整的js代码

let files = [];
let zNodes = [];
const treeId = 'fileTree'
const fileInput = $('#file_input');
fileInput.bind('change', function (e) {files = Array.from(e.target.files)createTree();
})
function initNodes() {zNodes = [];$.fn.zTree.init($("#fileTree"), setting, zNodes);
}
const setting = {}function createTree() {initNodes();const zTree = $.fn.zTree.getZTreeObj(treeId);let nodes = [];files.forEach(file => {nodes = zTree.transformToArray(zTree.getNodes());const names = file.webkitRelativePath.split('/')if (nodes.length == 0) {zTree.addNodes(null, 0, {id: names[0],parentId: null,name: names[0],filePath: names[0],})}})
}

当前实现效果,有一个根节点了:

(3)处理其他节点。

对names进行循环,相当于从外到内逐层添加节点,先找到父节点,就可以使用addNodes方法添加当前节点。如果是文件(即在最后一层),需要加上filePath记录相对路径的信息。

files.forEach(file => {nodes = zTree.transformToArray(zTree.getNodes());const names = file.webkitRelativePath.split('/')if (nodes.length == 0) {zTree.addNodes(null, 0, {id: names[0],parentId: null,name: names[0],filePath: names[0],})}names.forEach((name, index) => {// index==0时就是name就是根节点if (index >= 1) {nodes = zTree.transformToArray(zTree.getNodes());// 找父节点const parentId = names[index - 1]const pNode = nodes.find(node => node.id == parentId)let newNode = {id: name,parentId: parentId,name: name}if (name == names[names.length - 1]) {newNode.filePath = file.webkitRelativePath}zTree.addNodes(pNode, 0, newNode)}})
})

实现效果:

6、使用input框上传文件夹并且展示成树形结构的功能已经实现了,下边来做拖拽上传文件夹。

(1)先了解几个拖拽相关API:

拖拽容器相关事件:

(2)在dragenter的时候,可以把容器中的内容显示为“请释放鼠标”,这样会有比较好的体验效果;dragleave的时候,内容显示为“请将文件夹拖拽到此”;drop的时候,要阻止默认事件,否则浏览器会尝试打开文件夹,并且需要恢复原有内容。

const drop = $('#drop');
const originHTML = drop.html();
drop.bind('dragenter', function (e) {drop.html('请释放鼠标')
})
drop.bind('dragleave', function (e) {drop.html('请将文件夹拖拽到此')
})$(document).bind('dragover', function (e) {e.preventDefault();return false
})
$(document).bind('drop', function (e) {e.preventDefault();drop.html(originHTML)return false
}) 

(3)如果是在drop容器中发生drop事件,获取拖拽携带的信息。通过event.originalEvent.dataTransfer.items可以获取到所有的传输对象的信息。在此需要将files清空。

drop.bind('drop', function (e) {files = [];const items = e.originalEvent.dataTransfer.items;for (let i = 0; i < items.length; i++) {const item = items[i]console.log(item);}
})

如果是文件夹的话,item的信息长这样:

(4)对于文件夹使用item.webkitGetAsEntry()

可以查看一下关于这一方法的解释

DataTransferItem.webkitGetAsEntry() - Web API 接口参考 | MDN

drop.bind('drop', function (e) {files = [];const items = e.originalEvent.dataTransfer.items;for (let i = 0; i < items.length; i++) {const item = items[i]if (item.kind == 'file') {let entry = item.webkitGetAsEntry();console.log(entry);}}
})

打印出来长这样:

可以通过isFile判断是不是文件,通过isDirectory判断是不是文件夹

(5)这里如果不是文件夹,需要提示错误(可以最后再加)

drop.bind('drop', function (e) {files = [];const items = e.originalEvent.dataTransfer.items;for (let i = 0; i < items.length; i++) {const item = items[i]if (item.kind == 'file') {let entry = item.webkitGetAsEntry();if (!entry.isDirectory) {alert('请上传文件夹')return}}}
})

(6)接下来就需要解析这个文件夹了。解析文件夹需要放到一个递归方法中。

我们可以通过__proto__看一下这个entry的原型是什么:

文件entry的原型:

文件夹entry的原型:

(7)如果是文件的话,可以通过file()方法, 来创建一个拥有当前文件信息的文件

可以查看一下官方解释:FileSystemFileEntry - Web APIs | MDN

function getFilesFromEntry(entry) {if (entry.isFile) {entry.file(file => {console.log(file);},err => {console.log(err);})} else {console.log(entry.__proto__);}
}

可以看到当前file是没有相对路径的。这个属性是不能手动加上的,所以加一个filePath属性指向相对路径,并且push到files数组中。

function getFilesFromEntry(entry) {if (entry.isFile) {entry.file(file => {file.filePath = entry.fullPath.slice(1)files.push(file)},err => {console.log(err);})} else {console.log(entry.__proto__);}
}

(8)如果是文件夹可以使用createReader()方法来解析这个文件夹

FileSystemDirectoryEntry.createReader() - Web APIs | MDN

这个方法会返回一个对象,这个对象可以用来读文件夹中所有的entries

FileSystemDirectoryReader - Web APIs | MDN

readEntries这个方法就可以返回文件夹里的所有entries

function getFilesFromEntry(entry) {if (entry.isFile) {entry.file(file => {file.filePath = entry.fullPath.slice(1)files.push(file)},err => {console.log(err);})} else {const entryReader = entry.createReader()entryReader.readEntries((results) => {results.forEach(result => {getFilesFromEntry(result);})},(error) => {console.log(error);});}
}

(9)打印一下files:

当解析完所有文件的时候需要调用createTree方法。怎么判断解析完了所有文件呢?

可能需要先遍历一边所有的entry先算一下count

function getCount(entry) {if (entry.isFile) {entry.file(file => {count++},err => {console.log(err);})} else {const entryReader = entry.createReader()entryReader.readEntries((results) => {results.forEach(result => {getCount(result);})},(error) => {console.log(error);});}
}

在第二次遍历的时候比较count和files.length如果相等,则调用createTree方法创建文件结构树。

至此拖拽功能实现

完整代码:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script type="text/javascript" src="zTree_v3-master/js/jquery-1.4.4.min.js"></script><script type="text/javascript" src="zTree_v3-master/js/jquery.ztree.core.js"></script><script type="text/javascript" src="zTree_v3-master/js/jquery.ztree.excheck.js"></script><script type="text/javascript" src="zTree_v3-master/js/jquery.ztree.exedit.js"></script><link rel="stylesheet" href="zTree_v3-master/css/zTreeStyle/zTreeStyle.css" type="text/css"><title>Document</title><style>li {list-style: none;}a {cursor: pointer;}.icon-download {font-size: 30px;cursor: pointer;}#drop {width: 500px;height: 500px;border: 1px solid black;float: left;}#folder_container {float: left;}</style>
</head><body><!--文件夹下所有文件的信息 --><div id="drop"><input type="file" id="file_input" name="folder" webkitdirectory /><div style="text-indent: 10px"> 将文件夹拖到这里进行上传</div></div><!-- 树 --><ul id="folder_container"><ul id="fileTree" class="ztree"></ul></ul><script>let files = [];let zNodes = [];const treeId = 'fileTree'const fileInput = $('#file_input');fileInput.bind('change', function (e) {files = Array.from(e.target.files)createTree();})function initNodes() {zNodes = [];$.fn.zTree.init($("#fileTree"), setting, zNodes);}const setting = {}function createTree() {console.log(files);initNodes();const zTree = $.fn.zTree.getZTreeObj(treeId);let nodes = [];files.forEach(file => {const filePath = file.webkitRelativePath == '' ? file.filePath : file.webkitRelativePathnodes = zTree.transformToArray(zTree.getNodes());const names = filePath.split('/')if (nodes.length == 0) {zTree.addNodes(null, 0, {id: names[0],parentId: null,name: names[0],filePath: names[0],})}names.forEach((name, index) => {// index==0时就是name就是根节点if (index >= 1) {nodes = zTree.transformToArray(zTree.getNodes());// 找父节点const parentId = names[index - 1]const pNode = nodes.find(node => node.id == parentId)let newNode = {id: name,parentId: parentId,name: name}if (name == names[names.length - 1]) {newNode.filePath = filePath}zTree.addNodes(pNode, 0, newNode)}})})}// 拖拽const drop = $('#drop');const originHTML = drop.html();drop.bind('dragenter', function (e) {drop.html('请释放鼠标')})drop.bind('dragleave', function (e) {drop.html('请将文件夹拖拽到此')})$(document).bind('dragover', function (e) {e.preventDefault();return false})$(document).bind('drop', function (e) {e.preventDefault();drop.html(originHTML)return false})let count = 0drop.bind('drop', function (e) {files = [];const items = e.originalEvent.dataTransfer.items;for (let i = 0; i < items.length; i++) {const item = items[i]if (item.kind == 'file') {let entry = item.webkitGetAsEntry();if (!entry.isDirectory) {alert('请上传文件夹')return}//递归解析文件夹getCount(entry)setTimeout(() => {getFilesFromEntry(entry)}, 300)}}})function getCount(entry) {if (entry.isFile) {entry.file(file => {count++},err => {console.log(err);})} else {const entryReader = entry.createReader()entryReader.readEntries((results) => {results.forEach(result => {getCount(result);})},(error) => {console.log(error);});}}function getFilesFromEntry(entry) {if (entry.isFile) {entry.file(file => {file.filePath = entry.fullPath.slice(1)files.push(file)if (files.length == count) createTree();},err => {console.log(err);})} else {const entryReader = entry.createReader()entryReader.readEntries((results) => {results.forEach(result => {getFilesFromEntry(result);})},(error) => {console.log(error);});}}</script>
</body>
</html>

js使用input上传文件夹、拖拽上传文件夹并将文件夹结构展示为树形结构相关推荐

  1. vue——js实现图片/文件的拖拽上传(复制粘贴就能用,还有优化空间)

    首先先创建元素容器 <template><div id="drop"><span v-show="isUpload" class= ...

  2. 图片上传(支持拖拽上传)及列表图片预览

    要注意的是图片路径 先看效果图: 下面是代码啦: 前端ftl index: <#assign base=request.contextPath/> <!DOCTYPE HTML> ...

  3. JavaScript实现拖拽上传 解析 APK 信息

    点击上方蓝字,关注我们 技术栈 jquery 文件上传:jquery.fileupload,github 文档 apk 文件解析:app-info-parser,github 文档 参考:前端解析ip ...

  4. js实现文件拖拽上传并显示待上传的文件列表

    此文章中完整的代码在我的github中:https://github.com/LiuFeng1011/WebTest/tree/master/upload 首先实现html页面的内容: <bod ...

  5. Dropzone.js实现文件拖拽上传

    dropzone.js是一个开源的JavaScript库,提供 AJAX 异步文件上传功能,支持拖拽文件.支持最大文件大小.支持设置文件类型.支持预览上传结果,不依赖jQuery库. 使用Dropzo ...

  6. vue-simple-uploader实现多文件/文件夹以及可拖拽上传

    vue-simple-uploader的简单使用 1.效果图展示 2.安装 3.vue2使用(vue3使用会报错) 4.代码 vue-simple-uploader是基于simple-uploader ...

  7. 原生js实现拖拽上传文件

    原生js实现拖拽上传文件 <!DOCTYPE html> <html lang="en"><head><meta charset=&quo ...

  8. js 监听 复制图片 拖拽上传文件 并填充到markdown编辑器

    文章目录 效果 获取粘贴的文件 获取拖拽的文件 发送请求 生成markdown 语句 实现逻辑代码(主要实现) 后端代码 效果 获取粘贴的文件 const { clipboardData } = e; ...

  9. 实现拖拽上传文件的一款小控件——dropzone

    由于专注所以专业.非常多小巧的东西乍一看非常不起眼,却在特定的领域表现不俗,就是由于集中了热情. dropzone就是这样一款小控件,实现拖拽上传.它不依赖于其他像jquery等JS库.并且支持多方面 ...

最新文章

  1. 删除指定文件夹下的小于 4K的所用文件...
  2. 自定义HTTP标头:命名约定
  3. 诺贝尔物理学奖得主Arthur Ashkin去世,他发明了“激光镊子”,曾抱怨被诺奖遗忘...
  4. [zz]Ubuntu10.04源 更新源列表
  5. javascript笔记——点击按钮(或超链接)如何跳转到另外一个页面并执行目标页面的js函数...
  6. shell中echo使用单引号时输出单引号
  7. java中各进制之间的转换(十进制转十六进制、十进制转二进制、二进制转十进制、二进制转十六进制)...
  8. nginx+tomcat动静分离结构
  9. 【mongoDB运维篇③】replication set复制集
  10. 执行本地sql_实用!5个在线 SQL 数据库环境
  11. SQL Server读懂语句运行的统计信息 SET STATISTICS TIME IO PROFILE ON
  12. 漫谈 Clustering (番外篇): Expectation Maximization
  13. 人工智能:一种现代方法 第四版 翻译序言
  14. 可实现ffmpeg转码的cuda显卡
  15. HMI车载开发:汽车与Android的关系:Android Automotive
  16. python 文件格式转换_如何把txt文件转换成py文件
  17. python文件打包成exe是 upx不可用、找不到py文件_简单使用Pyinstaller将Python文件打包为可执行性exe-文件夹变成exe...
  18. 小写数字转大写金额php,php 金额小写数字转大写汉字
  19. 【数据api】数据API企业关键字模糊查询
  20. STM32开发入门及实战

热门文章

  1. 阳台做成榻榻米 阳台做成书房
  2. 汇编 INT 10H功能
  3. matlab里怎么计算期望,§7.4.2 利用MATLAB计算随机变量的期望和方差.pdf
  4. 2021-02-25 银行业十大主题记忆
  5. 镭速发布文件直传新功能,实现端到端快速传输
  6. adf的主要功能之一是_ADF 入门第一步系列
  7. linux去除内容重复行,Linux删除文本中的重复行 - 米扑博客
  8. 微信公众号自动回复服务器数据,[终极方案]解决微信公众号服务器配置启用后无法自动回...
  9. BaseRecyclerViewAdapterHelper源码解读(四) 上拉加载更多
  10. 大脑构造图与功能解析_大脑的结构和功能?