浏览器控制台中使用ajax下载文件通用代码(没有postman等情况下)

有时候,可能电脑里面没有postman(比如内网),然后又需要导出一些文件,前端又没有提供相应的功能(比如循环调用导出等),这时候我们就可以通过在控制台写代码的方式来实现了。这个还是在帮同事处理实施的问题时候想到的,当时在内网环境,同时导出4000家单位处理之后的数据系统会卡死,然后就用了这种方式,写代码循环了5次来分批导出。
注意:因为是在浏览器中使用,所以会有跨域问题,除非后端处理了跨域问题,否则只能请求当前页面的地址,总之就是一句话,你代码里面能发送的ajax请求,控制台里面也能。

代码实现

下面的代码定义了一个对象$$$,里面最主要的就是downLoad方法,后面就是调用这个方法来下载文件。在调用download方法之前,可能需要初始化请求方式(默认POST)和请求头等,具体可以看下面的属性介绍。download主要是通过XHR来发送ajax请求

const $$$ = {
    // 默认POST方法
    method: 'POST',
    // 请求头对象,可以是map类型,也可以是对象类型,如果有token等要放请求头的,可以设置这个值
    header: null,
    // 文件名称处理程序,如果为空,则使用时间戳
    fileNameHandler: null,
    /**
     * 下载文件的方法
     *
     * @param url 请求地址
     * @param data body体,可以为空
     */
    downLoad(url, data) {
        if (!url) {
            return console.error("地址不能为空");
        }
        if (!this.method) {
            return console.error("http请求方法为空");
        } else if (!['get', 'GET', 'post', 'POST'].includes(this.method)) {
            return console.error("http请求方法只能是get、post,当前请求方法:" + this.method);
        }
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'blob';
        const _this = this;
        xhr.onload = function () {
            if (this.status === 200) {
                // 创建一个新的Blob对象,从XHR的response中获取数据
                const blob = new Blob([this.response], {type: 'application/octet-stream'});

                let name;
                // 从函数里面获取名称
                if (!!_this.fileNameHandler && _this.fileNameHandler instanceof Function) {
                    name = _this.fileNameHandler(xhr);
                }
                if (!name) {
                    name = Date.now().toString();
                }

                // 创建一个a标签用于下载
                const a = document.createElement('a');
                a.href = window.URL.createObjectURL(blob); // 创建指向blob对象的URL
                a.download = decodeURI(name); // 设置下载文件的文件名
                a.style.display = 'none'; // 隐藏a标签
                // 将a标签添加到DOM中
                document.body.appendChild(a);
                // 触发a标签的点击事件,开始下载
                a.click();
                // 下载完成后,移除a标签
                document.body.removeChild(a);
            }
        };
        xhr.open(this.method, url);
        if (!!this.header) {
            // 如果是map类型的
            if (this.header instanceof Map) {
                this.header.forEach((value, key) => {
                    xhr.setRequestHeader(key, value);
                })
            } else if (this.header instanceof Object) {
                for (let [key, value] of Object.entries(this.header)) {
                    xhr.setRequestHeader(key, value);
                }
            } else {
                console.warn("header形参非对象或map类型,未设置到请求头中");
            }
        }
        let reqData = data;
        if (!!data) {
            if (data instanceof Object) {
                reqData = JSON.stringify(reqData);
            }
        }
        xhr.send(reqData);
    },
    setMethod(method) {
        this.method = method;
        return this;
    },
    setHeader(header) {
        if (header != null) {
            if (!(header instanceof Map) && !(header instanceof Object)) {
                console.error("header形参非对象或map类型,不可设置");
            }
        }
        this.header = header;
        return this;
    },
    setFileNameHandler(fileNameHandler) {
        this.fileNameHandler = fileNameHandler;
        return this;
    },
    /**
     * 添加头
     */
    addHeader(key, value) {
        if (!this.header) {
            this.header = {};
            this.header[key] = value;
        }
        if (!!key && !!value) {
            if (this.header instanceof Map) {
                this.header.set(key, value);
            } else if (this.header instanceof Object) {
                this.header[key] = value;
            }
        }
        return this;
    },
    /**
     * 删除头部
     * @param key 字符串类型删除单个,数组类型批量删除
     */
    deleteHeader(key) {
        if (key == null) {
            return;
        }
        const _this = this;

        if (!!this.header) {
            if (typeof key === 'string') {
                removeKey(key);
            } else if (key instanceof Array) {
                key.forEach(t => {
                    removeKey(t);
                })
            } else {
                console.warn("key非字符串类型或数组类型");
            }
        }

        function removeKey(k) {
            if (_this.header instanceof Map) {
                _this.header.delete(k);
            } else if (_this.header instanceof Object) {
                delete _this.header[k];
            }
        }
    }
}

属性

method

http请求方法,默认POST,可以通过$$$.method方式或者$$$.setMethod方式改成GET请求。

header

请求头,如果需要携带一些头部信息,就可以设置这个值,这个值类型可以是对象,也可以是map。只要不为空,就会添加到请求头中。

fileNameHandler

文件名称处理器,用来处理导出的文件名称,如果为空,则会使用时间戳当文件名。这个需要自定义,一般我们都会把文件名称放到Content-Disposition头中,这个和后端处理逻辑有关,可以根据自己的需要设置这个函数。
示例:

// 以下两种方式设置都行
$$$.fileNameHandler = (xhr) => {
		// 具体的文件名称处理逻辑,这里只是示例,我这边的是直接替换掉头部的字符然后返回
        return xhr.getResponseHeader('Content-Disposition').replace('attachment;filename=', '');
    }

$$$.setFileNameHandler((xhr) => {
        return xhr.getResponseHeader('Content-Disposition').replace('attachment;filename=', '');
    })

方法

为了方便赋值,里面有三个setXXX方法,都返回了this对象,可以链式调用。还有添加单个头部和删除头部的方法,主要是为了方便修改请求头参数。

浏览器控制台中使用

代码复制到控制台

进入了自己的系统之后,ctrl + c,ctrl +v把代码复制到控制台(也可以把代码保存成一个文件,然后拖到控制台,会自行输入到控制台中)。

初始化属性

这个按需配置,改成符合自己的,比如我这里,需要给头部加上token,而且请求是get

$$$.setHeader({'authorization': '992fbef034d74d3f8b853a8c70d52922'})
    .setMethod('get')
    .setFileNameHandler((xhr) => {
        return xhr.getResponseHeader('Content-Disposition').replace('attachment;filename=', '');
    })

调用下载方法

控制台中,直接调用download方法下载,如果是post请求,并且有body体,通过第二个形参传入就行。

// 输入完成之后,回车,就会下载了
$$$.downLoad('http://xxx.xxx.xxx:port/export?id=xxxx')
// 第二个参数如果不为空,就会加入到请求体中。如果是对象类型,内部会调用JSON.stringify()方法进行转换
$$$.downLoad('http://xxx.xxx.xxx:port/export?id=xxxx', {})

效果图:
在这里插入图片描述

控制台导出文件技巧

上面的只是单个文件导出,如果想实现其他的导出,我们可以变通一下

批量导出

id连续的批量导出

比如,我们需要导出id为1-500的数据文件,每个id一个文件,如果通过人为方式设置就太费时间了。可以在初始化一个变量i,然后通过setInterval定义一个定时器(这样我们可以设置一个间隔,不至于发送的太快了),定时器里面的逻辑每次调用下载一个文件,然后i自增,还要判断当前下载到了第几个,如果<=500就下载,下载完成之后记得清除定时器

const interval = setInterval(() => {
	if(i <= 500){
		// 执行下载逻辑,i++
	} else {
		clearInterval(interval);
	}
}, 3000); // 时间可以根据需要配置,我这里给了3000毫秒

随机的id,需要批量

可以在外部定义一个数组,其他的同上面。

页面中存在导出按钮,但是量太大会崩溃

有时候,可能一次性想导出大量单位的数据,但是后台会崩,也不太可能通过人为一次选一批,这样也太慢了。这种可以先把当前页面设置为禁止请求网络,然后选择要导出的所有,点击导出。再把网络打开。在network里面选中刚刚的那个请求,在请求荷载里面把请求的id集合保存为控制台变量(假设是通过id导出),然后控制台里面就能拿到这个变量列表了。再通过列表截取的方式,分成好几批来导出
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值