前端js如何实现多文件下载

539 阅读2分钟

场景:

在一个系统里,用户点击下载按钮,就能把几百个文件下载下来,并且按照分类分好文件夹和压缩。

痛点:

一次发送几百个请求到服务器,服务器是不能处理的,从而导致连接被取消。不同浏览器的同一域名下的最大请求并发数量是有限的,那下载文件的时间一长,后面的接口就会超时断开连接。所以,我们需要异步去处理这些请求。

实现思路

首先,我们实现一个异步下载文件的方法,一次只有10个并发请求。那我们的思路就是,把文件对象放数组,一开始取10个对象,请求文件链接下载文件,一个请求完了,就开始请求第11个对象文件,如此直到数组为空。

 const DownloadMultiFile = (list, opts) => {
        let fileList = [...list];
        let promiseFile = [];
        let max = 10;
        return new Promise((resolve) => {
            const downFile = () => {
                if (fileList.length) {
                    const url = fileList.shift().url;
                    const axiosFile = fetch(url, opts)
                    promiseFile.push(axiosFile);
                    // 一个文件处理完就再把文件推进来处理
                    axiosFile.then(() => {
                        downFile();
                    }).catch(() => {
                        downFile();
                    })
                } else {
                    // 所有的请求都有了结果以后,就可以resolve了,一次resolve之后,再调用也不会改变DownloadMultiFile的结果
                    Promise.allSettled(promiseFile).then(result => resolve(result));
                }

            }
            // 如果文件没有10个,就直接把文件一通下载
            const count = Math.min(fileList.length, max)
            // 前10个文件不需要异步
            for (let i = 0; i < count; i++) {
                downFile();
            }
        })
    }

到这里,很基础的文件下载就完成了。

如果文件比较机密,访问文件路径需要在请求链接拼上授权信息,那我们就需要在访问文件前先调用授权接口获取授权信息,并且缓存下来,在每次访问文件时都需要先看看授权信息过期了没,如果过期了需要重新获取。

授权的请求需要考虑同一时间被重复调用的问题。因为一开始是有10个请求的并发。

可以用一个字段判断接口是否在调用,调用前设为true,调用结束设为false,为true的时候,不允许再调用接口,但是这个比较没有美感,也不通用。可以利用了缓存promise结果的方式保证只调用一次接口。这是参考的kezy写的# typescript - promise缓存与复用

const authAction = () => {
        let result = null;
        let auth = null;
        let i = 0;
        const cache = (promise) => {
            console.log("cache", result)
            return result = result || promise().catch((err) => {
                result = null;
                return Promise.reject(err)
            })
        }
        const queryAuth = () => {
            return new Promise(resolve => {
                console.log("fetch")
                // auth = fetch() 
                setTimeout(() => {
                    const val = {
                        value: i++,
                        expiresTime: new Date().getTime() + 6000
                    }

                    resolve(val);
                }, 2000)
            })
        }
        return {
            getAuth: () => {
                // 授权过期要把缓存的promise清掉重新请求接口,接口的结果也要清空,防止后面并发时会把result清空导致缓存promise失效
                if (auth && auth.expiresTime < new Date().getTime()) {
                    result = null;
                    auth = null;
                }
                const queryResult = cache(queryAuth);
                queryResult.then(result => {
                    auth = result;
                })
                return queryResult;
            }
        }

    }

到这里,异步下载文件已经完成了。最后就是把文件放文件夹并压缩保存了。这里我使用的是jszip插件,具体用法我就不讲了,大家可以自己去官网查看api。