一般前端导出表格时,后台会有两种方式返给前端数据:
- 后台返回一个下载地址url。这种方式是后台得先生成文件保存在服务器某个地方,然后返回给前端url地址,前端直接拿到url地址一行代码去下载文件即可。这种有个问题就是后台得知道什么时候可以删除生成的那个文件。一般这种方式不推荐使用。
- 后台返回给前端的是一个二进制流,这种方式好处在于后台不用生成文件去保存,二进制流保存在内存中,不用占用服务器存储空间,不用考虑什么时候删除去文件。这种方式也是常见的一种方式。
下面我们主要讲一下第二种方式的使用和一些需要注意的地方:
首先我们先看最终要使用到的的正确结果代码:
1.下面代码是核心的处理方法,此处我封装成了一个工具方法在整个项目中使用
export const exportExcel = (data, fileName = "导出表格.xlsx") => {
let blobObj = new Blob([data], { type: "application/vnd.ms-excel" });
// for IE
if (window.navigator && window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blobObj, fileName);
} else {
let blobUrl = window.URL.createObjectURL(blobObj);
const link = document.createElement("a"); // 创建a标签
link.href = blobUrl;
link.download = fileName;
link.click(); // 模拟点击a标签
window.URL.revokeObjectURL(link.href);
}
}
2.下面代码是在导出方法里拿到后台数据后进一步作出判断再去调用上面的核心方法
if (res.data.type == "application/octet-stream") {
exportExcel(res.data, "text.xlsx");
} else {
this.$message.error("导出失败");
}
3.下面代码是axios自己封装的一个请求方法,即request方法,其实就是请求后台接口的
export function exportTodayCity(data) {
return request({
url: '/second/seasonal/exportTodayCity',
method: 'post',
data,
responseType: "blob"
})
}
现在我们分析一下上面三点代码的作用及原因:
先讲一下第一点,首先方法有两个参数,分别是data,fileName,data表示处理二进制流为blob对象的数据,fileName表示下载后文件的名字,这里给了一个默认名字,防止传入的名字为空。第一行代码目的是为了转换成自己所需要的新的Blob对象,下面看一下Blob构造函数的具体解释:
var aBlob = new Blob( array, options );
参数:
- array 是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings会被编码为UTF-8
- options 是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
- type,默认值为 “”,它代表了将会被放入到blob中的数组内容的MIME类型。
- endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个: “native”,代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 “transparent”,代表会保持blob中保存的结束符不变。
然后再说这里的参数意义,data为处理二进制流为blob对象的数据,主要说一下这里的type参数里面的值的意义,目的就是改变MIME类型,使下载的文件后缀名为xls,即为表格文件。注意:(这里要说一下,如果 fileName那里的值只是个字符串名字,没有xls或者xlsx后缀名,则这里必须要写这个 type参数并值为application/vnd.ms-excel
,这样生成的文件默认就是以.xls为后缀名的表格文件了,否则有问题。当然了,如果 fileName那里已经是包含了xls或xlsx这样的后缀名了,则这里的 type参数可写可不写,写了也不会冲突,无非最终下载下的文件后缀名是与你 fileName那里的后缀名保持一致)。
这里如果有人想知道MIME类型都有哪些,请看W3school MIME类型 或 MDN MIME类型
后面的代码中主要做了浏览器兼容,其中msSaveBlob为了兼容IE下载导出,具体意义可查看MDN msSaveBlob
再后面就是使用 createObjectURL 创建一个临时的URL,然后创建一个a标签,模拟点击事件,从而进行下载导出,最后通过 revokeObjectURL释放创建的引用即可。对于这两个API不了解的可自行百度查看,也可以直接在MDN上查看。
第一点讲完了,我们再来看第二点,代码不多,这里主要做了判断处理,目的是为了处理后台导出失败时的情况,因为我们返回拿到的数据会被转换成blob类型,具体原因待会看第三点再说。这里就会出现不管成功还是失败都是blob类型的响应数据,就不能用平时json类型那种用code码来做判断了。这里是通过blob对象里的type字段值来做判断处理的,因为不管失败还是成功,他的blob结构一样,无非是type值不一样,所以就使用这个字段值作为成功与否的判断就行了。成功时type会返回application/octet-stream
,失败时,谷歌和火狐会返回application/json
,IE浏览器会返回application/json; charset=utf-8
,故这里使用成功时的返回值作为成功与否的判断。当然了,这种要保证这个导出接口后台返回的只有这两种type值,不存在其他情况。按理来说,这里也就两种type情况,不会有第三种出现。
再说最后一点,这里主要加一个字段responseType:"blob"
,因为接受的是二进制流,不是json格式数据,所以这里需要设置响应数据为blob对象,一般平时JSON格式请求只需要url,method,data三个参数即可,但这里导出下载一定要加这一行代码,因为使用axios请求时,responseType默认的是json。
额外说一下,刚才上面最后一点讲的设置responseType:"blob"
这个字段值,其实这里也可以设置成responseType:"arraybuffer"
,一样可以正常导出下载。这里就有人问了,那两个有啥区别?我只能简略地告诉你它们的区别:Blob 用于操作二进制文件,ArrayBuffer 用于操作内存。ECMAScript 6 入门里面这样说的:
如果明确知道返回的二进制数据类型,可以把返回类型(responseType)设为arraybuffer;如果不知道,就设为blob
blob 与 arraybuffer的具体详细内容可以参考 ArrayBuffer 对象,Blob 对象
到此,基本上该说的已经说完了,如有没有讲到的地方,可以留言告诉我,看到一定回复,好了,该去上个厕所了。。。
------------------------------------------------2020.08.31更新--------------------------------------------------------
之前说的第二点那里,导出失败的话,返回的是json格式,那里直接输出的提示“导出失败”,如果可能我们需要读取后台返回的信息,那就得考虑从后台返回的字段里读值了,而这里我们默认是以blob格式接收的,所以,我们此时要想办法将blob对象数据转换回json格式后再进一步操作即可。下面就是具体实现代码:
将上面第二点里的this.$message.error("导出失败");
替换为下面的代码即可。
let reader = new FileReader();
reader.readAsText(res.data); // res.data 是接收后台的blob格式数据对象
reader.onload = (e) => {
let res = JSON.parse(e.target.result);
if (res.code == 400) {
this.$message.error(res.msg);
}
};
当然了,具体进行什么操作看自己业务需求进行稍加改动即可。
------------------------------------------------2021.02.24更新--------------------------------------------------------
这里补充一下关于导入的实现代码(结合vue,elementUI):
直接看代码:
需要定义和引入的:
import { getToken } from "@/utils/auth"; //拿到token
import { Loading } from "element-ui"; //引入Loading
headerMsg: { Authorization: getToken() },
load: null,
下面部分参数含义:
action 里面是导入文件的接口地址。
headers 放的是token。
accept 表示接受文件类型,这里是excel表格类型的文件。
<el-upload
action="/admin/store/importStore"
:headers="headerMsg"
:show-file-list="false"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
:on-success="handleImportSuccess"
:on-error="handleImportError"
:on-progress="handlePending"
>
<svg class="svg-icon" aria-hidden="true">
<use xlink:href="#icon-import" />
</svg>
<span class="icon-left">导入</span>
</el-upload>
上面参数方法的具体实现代码:
handlePending() {
this.load = Loading.service({ text: "导入中", fullscreen: true });
},
handleImportSuccess(res) {
this.$nextTick(() => {
this.load.close();
});
if (res.code == 200) {
this.getStoreList(); //查询页面列表数据
this.$message({
message: res.msg,
type: "success",
});
} else {
this.$message({
message: res.msg,
type: "error",
});
}
},
handleImportError() {
this.$nextTick(() => {
this.load.close();
});
this.$message({
message: "导入失败",
type: "error",
});
},