1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > js使用input上传文件夹 拖拽上传文件夹并将文件夹结构展示为树形结构

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

时间:2023-11-20 16:10:00

相关推荐

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

一、实现效果

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

二、代码实现

由于需要使用树形插件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>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。