有时候,可能电脑里面没有
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请求方法为空");}elseif(!['get','GET','post','POST'].includes(this.method)){return console.error("http请求方法只能是get、post,当前请求方法:"+this.method);}const xhr =newXMLHttpRequest();
xhr.responseType ='blob';const _this =this;
xhr.onload=function(){if(this.status ===200){// 创建一个新的Blob对象,从XHR的response中获取数据const blob =newBlob([this.response],{type:'application/octet-stream'});let name;// 从函数里面获取名称if(!!_this.fileNameHandler && _this.fileNameHandler instanceofFunction){
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 instanceofMap){this.header.forEach((value, key)=>{
xhr.setRequestHeader(key, value);})}elseif(this.header instanceofObject){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;returnthis;},setHeader(header){this.header = header;returnthis;},setFileNameHandler(fileNameHandler){this.fileNameHandler = fileNameHandler;returnthis;}}
属性
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')
$$$.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
导出),然后控制台里面就能拿到这个变量列表了。再通过列表截取的方式,分成好几批来导出
版权归原作者 星月昭铭 所有, 如有侵权,请联系我们删除。