写在前面
在实际开发过程中经常会碰到用户要下载或者导出一个文件的需求。传统的做法是在后端存储或者即时生成一个文件来提供下载功能,这样的优势是可以做权限控制、方便数据二次处理,但缺点是需要额外发起请求、增大服务端压力、下载速度慢。但随着HTML5的标准发布,我大前端已经完全可以独立实现文件下载与导出啦~
利用a标签的 download 属性下载文件
download属性指示浏览器下载 URL而不是导航到它,因此将提示用户将其保存为本地文件。如果属性有一个值,那么此值将在下载保存过程中作为预填充的文件名(如果用户需要,仍然可以更改文件名)。此属性对允许的值没有限制,但是 / 和 \ 会被转换为下划线。大多数文件系统限制了文件名中的标点符号,故此,浏览器将相应地调整建议的文件名。
<a download="文件名" href="文件地址">下载测试</a>
需要注意的是:
- download 仅适用于同源 URL,但是可以使用 blob: URL 和 data: URL。
- 如果 HTTP 头中的
Content-Disposition
属性赋予了一个不同于此属性的文件名,HTTP 头属性优先于此属性。 - 如果 HTTP 头属性
Content-Disposition
被设置为inline 即Content-Disposition='inline'
,那么 Firefox 优先考虑 HTTP 头Content-Disposition
download 属性。
生成Data URLs 并下载文件
Data URL
即前缀为 data:
协议的URL,其允许内容创建者向文档中嵌入小文件。它 由四个部分组成:前缀(data:)、指示数据类型的MIME类型、如果非文本则为可选的base64标记、数据本身。
data:[<mediatype>][;base64],<data>
mediatype 是个 MIME 类型的字符串
例如 "image/jpeg" 表示 JPEG 图像文件。
如果被省略,则默认值为 text/plain;charset=US-ASCII
如果数据是文本类型,你可以直接将文本嵌入
如果是二进制数据,你可以将数据进行base64编码之后再进行嵌入。
导出文件代码示例:
//导出Json文件
exportJson(){
const downloadData = {
name:"April",
ager:"18",
hobby:"学习"
};
let dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(downloadData));
let downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", "文件名.json")
downloadAnchorNode.click();
downloadAnchorNode.remove();
},
生成blob: URL 并下载文件
Blob()
构造函数返回一个新的 Blob 对象。 blob的内容由参数数组中给出的值的串联组成。
let 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中保存的结束符不变 。
URL.createObjectURL()
静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
objectURL = URL.createObjectURL(object);
导出文件代码示例:
exportJson() {
const downloadData = {
name: "April",
ager: "18",
hobby: "学习"
};
let blob = new Blob(
[JSON.stringify(downloadData, null, 2)],
{type: 'application/json'});
let url = URL.createObjectURL(blob);
let downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute("href", url);
downloadAnchorNode.setAttribute("download", "文件名.json")
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
点击下载图片
虽然目前浏览器都支持保存图片到本地的功能(右键>图片另存为)但是实际开发中会涉及到批量下载图片、Canvas绘图的保存功能,应运上面的知识,我大前端也可以轻松实现。代码如下:
<button @click="downloadImg">下载图片</button>
// 通过src获取图片的blob对象
getImageBlob(url, cb) {
let xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.responseType = "blob";
xhr.onload = function () {
if (this.status == 200) {
cb(this.response);
}
};
xhr.send();
},
// 点击下载图片
downloadImg(){
let reader = new FileReader();
this.getImageBlob('https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/v3/static/img/simplify-logo.3e3c253.svg~tplv-t2oaga2asx-image.image', function(blob){
// 读取来看下下载的内容 最终生成的字符串
reader.readAsDataURL(blob);
// 生成下载用的URL对象
let url = URL.createObjectURL(blob);
// 生成一个a标签,并模拟点击,即可下载,批量下载同理
let downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute("href", url);
downloadAnchorNode.setAttribute("download", "下载图片")
downloadAnchorNode.click();
downloadAnchorNode.remove();
})
},
下载表格
推荐由SheetJS出品的js-xlsx是一款非常方便的只需要纯JS即可读取和导出excel的工具库,功能强大,支持格式众多,支持xls、xlsx、ods(一种OpenOffice专有表格文件格式)等十几种格式。本文全部都是以xlsx格式为例。 官方github:github.com/SheetJS/js-…
// 测试用的数据
staff: [
{name: "April", job: "programmer", age: "18", hobby: "study"},
{name: "Shawn", job: "student", age: "8", hobby: "study"},
{name: "Leo", job: "teacher", age: "28", hobby: "play"},
{name: "Todd", job: "programmer", age: "19", hobby: "sleep"},
{name: "Scoot", job: "cook", age: "38", hobby: "paintting"},
]
安装xlsx
npm install xlsx --save
import XLSX from 'xlsx';
auto_width(ws, data) {
/*set worksheet max width per col*/
const colWidth = data.map(row => row.map(val => {
/*if null/undefined*/
if (val == null) {
return {'wch': 10};
}
/*if chinese*/
else if (val.toString().charCodeAt(0) > 255) {
return {'wch': val.toString().length * 2};
} else {
return {'wch': val.toString().length};
}
}))
/*start in the first row*/
let result = colWidth[0];
for (let i = 1; i < colWidth.length; i++) {
for (let j = 0; j < colWidth[i].length; j++) {
if (result[j]['wch'] < colWidth[i][j]['wch']) {
result[j]['wch'] = colWidth[i][j]['wch'];
}
}
}
ws['!cols'] = result;
},
json_to_array(key, jsonData) {
return jsonData.map(v => key.map(j => {
return v[j]
}));
},
export_array_to_excel({key, data, title, filename, autoWidth}) {
const wb = XLSX.utils.book_new();
const arr = this.json_to_array(key, data);
arr.unshift(title);
const ws = XLSX.utils.aoa_to_sheet(arr);
if (autoWidth) {
this.auto_width(ws, arr);
}
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb, filename + '.xlsx');
},
exportExcel() {
let staff = this.staff;
const params = {
title: ['姓名', '工作', '年龄', '爱好'],
key: ['name', 'job', 'age', 'hobby'],
data: staff,
autoWidth: true,
filename: '文件名'
}
this.export_array_to_excel(params)
},
读取上传文件的数据
想要读取Blob数据的唯一方法是FileReader。
FileReader
对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File
或 Blob
对象指定要读取的文件或数据。
其中File
对象可以是来自用户在一个<input>
元素上选择文件后返回的FileList
对象,也可以来自拖放操作生成的 DataTransfer
对象,还可以是来自在一个HTMLCanvasElement
上执行mozGetAsFile()
方法后返回结果。
包含5个方法:
-
FileReader.abort()
中止读取操作。在返回时,readyState属性为DONE。 -
FileReader.readAsArrayBuffer()
开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象. -
FileReader.readAsBinaryString()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。 -
FileReader.readAsDataURL()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容。 -
FileReader.readAsText()
开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。
将上传的文件读取为字符串的代码示例
handleUpload(blob) {
// 新建一个FileReader
const reader = new FileReader()
// 读取文件
reader.readAsText(blob, "UTF-8")
// 读取完文件之后会回来这里
reader.onload = function (e) {
// 读取文件内容
const fileString = e.target.result
// 接下来可对文件内容进行处理
const myData = JSON.parse(fileString);
console.log(myData) // 打印读取到的内容
}
},
将上传的文件读取为URL格式的字符串的代码示例
handleUpload(file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
const urlStr = reader.result
console.log(urlStr)
}
}
读取上传Excel文件
同样推荐由SheetJS出品的js-xlsx
import XLSX from 'xlsx';
handleUpload(file) {
let that = this;
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = function (evt) {
var data = evt.target.result;
var workbook = XLSX.read(data, {
type: 'binary'
}) // 以二进制流方式读取得到整份excel表格对象
}
reader.onload = function (evt) {
//当读取完成后回调这个函数,然后此时文件的内容存储到了result中,直接操作即可
try {
var data = evt.target.result,
workbook = XLSX.read(data, {
type: 'binary'
}), // 以二进制流方式读取得到整份excel表格对象
buildings = []; // 存储获取到的数据
var fromTo = '';
// 遍历每张表读取
for (var sheet in workbook.Sheets) {
if (workbook.Sheets.hasOwnProperty(sheet)) {
fromTo = workbook.Sheets[sheet]['!ref'];
buildings = buildings.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]));
}
}
console.log(buildings) // 文件内容
} catch (e) {
console.log('文件类型不正确', e);
return;
}
}
return false;
},