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

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

代码实现

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

javascript 复制代码
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);
        xhr.setRequestHeader("Content-Type", "application/json, text/plain, */*");
        xhr.setRequestHeader("accept", "application/json;charset=UTF-8");
        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类型,未设置到请求头中");
            }
        }
        xhr.send(data);
    },
    setMethod(method) {
        this.method = method;
        return this;
    },
    setHeader(header) {
        this.header = header;
        return this;
    },
    setFileNameHandler(fileNameHandler) {
        this.fileNameHandler = fileNameHandler;
        return this;
    }
}

属性

method

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

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

fileNameHandler

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

示例:

javascript 复制代码
// 以下两种方式设置都行
$$$.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

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

调用下载方法

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

javascript 复制代码
// 输入完成之后,回车,就会下载了
$$$.downLoad('http://xxx.xxx.xxx:port/export?id=xxxx')

$$$.downLoad('http://xxx.xxx.xxx:port/export?id=xxxx', {})

效果图:

控制台导出文件技巧

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

批量导出

id连续的批量导出

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

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

随机的id,需要批量

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

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

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

相关推荐
腾讯TNTWeb前端团队5 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom10 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom10 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom10 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom10 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试