HTML5+原生JS实现服务器端目录树中多文件下载

HTML5+原生JS实现服务器端目录树中多文件下载

作者:云荒杯倾
作者博客

需求

需求是这样的,服务器上有一个目录,目录下面可能既有文件又有其他目录,其他目录下面也一样,既可能有文件也有目录;浏览器要显示有这个目录,并提供这个目录下所有文件的一键下载功能。

实现原理

实现这个需求,本程序考虑到的知识点有html5 <a>标签的下载功能、数据结构中树的广度优先遍历算法、一点http知识和一点JS局限性的知识。

HTML5 <a>标签的下载功能

首先说HTML5 <a>标签的下载功能
HTML5的<a>标签有一个download属性,如果你设置了这个属性,那么点击这个链接,就不再是跳转到这个<a>标签href引用的地址了,而是直接去下载href所代表的一个文件。

<a herf="./bbb.html">这是超链接</a>

这样,点击这是超链接几个字,只能页面跳转到bbb.html。
而,

<a herf="./bbb.html" download="ccc.html">这是超链接</a>

这样,加了download属性后,你再点击这是超链接几个字,就直接下载bbb.html到你的本地机器(客户端)了,且文件会改名为“ccc.html”。如果只写download,则是保持原始文件名下载。

一点http知识

http是不提供目录遍历的,所以暂时不知道其他实现方法是否碰到了这个点,所以导致程序失败。某种程度上,本文的程序算是避开了这个点?

一点JS局限性的知识

JS语言跑在浏览器的话,是不能操作本地(客户端)太多东西的,其中就包括一些文件操作,而文件夹创建操作就包括在这些限制操作之内。这也是无法实现将服务器上的一个目录按照其目录结构原样拷贝到本地(客户端)的原因。

看过一篇博文的举例很好,他说,如果我有一个网站,你是访客,你访问,点击了某个链接,我就把网站的一个目录树(假设有一百万个txt),拷贝到你的本地,你会不会恼火?而且,之所以浏览器不提供这些功能,主要是出于客户端安全方面的考虑。

做这个需求的时候,看到IE上有一个activexObject对象,可以在本地创建文件夹。但是想想就只有IE支持,还是算了。

综上,本程序是将服务器上一个有层次的目录树里面所有的文件,通过浏览器下载到本地同一个文件夹下的。

不过因为下载一个目录树的功能,最主要的考察点还是树状数据结构的遍历,如果哪天浏览器都实现了本地创建文件夹,本程序的代码仍有参考价值。

目录树的广度优先遍历及本程序的实现思路

本例的思路是,前后端定义一个目录树的接口。

前端通过xhr拿到数据。

一层一层遍历这颗目录树,直到遍历完这个目录树下所有文件,也就能拿到这所有文件的URL了。假设一共有n个文件,也就是n个URL。

拿到n个URL后,注册这个目录对应的下载该目录下所有文件按钮的监听器,监听器内创建n个html5 <a>标签,<a>标签的href分别赋值url路径,所有<a>标签设置download属性。

如此,就实现了加载HTML,HTML上有一个代表着服务器上一个目录的目录名,目录名后面有一个下载该目录下所有文件的按钮,点击该按钮,就从服务器下载了该目录下所有文件到本地。

本文定义的目录树结构

目录树接口定义如下,一个目录要有name(也就是URL),file是该层目录下裸露的文件,childDir是该层有的下一层的目录,如此往复。。。如果到了某一目录,既没有文件有没有目录,那么name就定义为[]数组,childDir也定义为[]数组。

本例中的目录树是:根目录./chrome61module,其下有三个文件,分别是aa.css,aa.js,module-test.html,还有一个目录bb;bb目录下只有两个文件bb.js和bb.html。

//假设这是从服务器取来的a链接的目录结构
        var rootDir = {
            "name":"./chrome61module",
            "file":["/aa.css","/aa.js","/module-test.html"],
            "childDir":[{
                "name":"./chrome61module/bb",
                "file":["/bb.js","/bb.html"],
                "childDir":[]
            },{}]
        };

效果展示


图1 HTML页面


图2 点击下载该目录下所有文件Chrome会提示你是否要下载多个文件


图3 本例中下载成功的五个文件

代码展示

代码比较简单,就直接放了,大家直接自己粘贴了,就能运行,注意修改目录树结构为你自己的

<body>
    <a href="./chrome61module" id="aDir">chrome61module目录</a>
    <button id="downloadDir">下载目录下所有文件</button>
    <script>
        //假设这是从服务器取来的a链接的目录结构
        var rootDir = {
            "name":"./chrome61module",
            "file":["/aa.css","/aa.js","/module-test.html"],
            "childDir":[{
                "name":"./chrome61module/bb",
                "file":["/bb.js","/bb.html"],
                "childDir":[]
            },{}]
        };
        
        //DOM
        var btnDownload = document.getElementById("downloadDir");
        var aDir = document.getElementById("aDir");
        //按钮监听器
        btnDownload.addEventListener("click",downloadDir);
        function downloadDir() {
            //处理某一层
            function oneTreeLayerProcess(dir) {
                if(JSON.stringify(dir) !== "{}"){
                    if(dir.file.length !== 0){
                        for(let i = 0; i< dir.file.length; i++){
                            var j = document.createElement("a");
                            j.href = dir.name + dir.file[i];
                            j.download = dir.file[i].slice(1);
                            j.click();
                        }
                    }
                    if(dir.childDir.length !== 0){
                        for(let i =0; i < dir.childDir.length; i++){
                            //递归
                            oneTreeLayerProcess(dir.childDir[i]);
                        }
                    }
                }
            }
            oneTreeLayerProcess(rootDir);
        }
        
    </script>
</body>

总结

1、由于http和JS能力的限制,目前仅能实现文件下载,不能实现文件夹下载或者文件夹本地创建,因此想原样拷贝服务器上一个目录到本地机器,使用JS目前较难做到。

2、由于本例实现的将一个目录树下所有文件都平行下载到本地的同一个文件夹下。造成客户端即使下载了也无法理解服务器上原目录树的困难。
一点改进是,如果使用download=“”,将“”内的文件名改为包含路径的文件名,也许下载以后,客户端可以获悉其原来在服务器上的对应的目录层级,从而可以自己后续处理。

3、如果可能,还是按照压缩包的形式下载一个目录也许更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值