值得注意
下载文件,清除缓存策略(最好后端接口处理,前端处理有些浏览器不识别)
后端: 接口再头部添加清除缓存标记
前端: 请求连接后面添加时间戳或者随机数a标签下载文件,base64 url文件过大无法下载问题;可以将文件存入内存createObjectURL中,用虚拟内存地址指向
总结
- 所有情况通用的方式: 后端设置下载请求的响应头 Content-Disposition: attachment;
- filename="filename.jpg"
- attachment 表示让浏览器强制下载
- filename 用于设置下载弹出框里预填的文件名
- 非跨域情况下 给a标签加上 download 属性,如 <a href="url" download="xxx.png"></a>
- download 里写文件名 注意后缀 (值非必填)
- 通过请求解决跨域问题 动态创建a标签通过blob形式下载 具体看下面解析
- 文件下载通常有以下3种方式
1、<a href="http://localhost:8080/upload/user.png">下载</a> a标签访问文件地址
2、window.open('http://localhost:8080/upload/user.png') 打开文件地址
3、后端提供一个接口 /api/download 通过接口返回文件流
浏览器通过请求头Content-Type中的MIME类型(媒体类型,通常称为 Multipurpose
Internet Mail Extensions 或 MIME 类型,如 :image/jpeg application/pdf)识别数据类型,对相应的数据做出相应处理,对于图像文本等浏览器可以直接打开的文件,默认处理方式就是打开,为了避免浏览器直接打开文件我们需要做一些处理;
此方式最为简易,只需要知道文件在服务器上的地址,就可以通过a标签实现下载
<a href="https://.../158ac1e6917445a4aa384a2a7209445a.xlsx" download="test">下载文件</a>
<a href="https://.../6d0e6934246c4ba9ba1a43c6992836ca.png" download="test">下载图片</a>
已知文件的地址,可以通过上面的方式将地址放入href属性内,download属性存放下载文件的名称,此属性为必须。
若文件地址为异步获取,即点击下载/导出按钮时才会从接口拿,则可以通过js插入a标签来实现。demo如下:
异步获取文件路径之后执行以下代码即可自动下载
*以下方式都可以避免浏览器直接打开文件
一、无监听下载实现
1、使用a标签下载 (单个下载)
须知: 当url是同源(同域名、同协议、同端口号)时,这种情况用 a标签加download属性的方式即可,download属性指示浏览器该下载而不是打开该文件,同时该属性值即下载时的文件名
<el-button type="text" @click.stop="downModel">下载模板</el-button>
// 下载模板
downModel() {
let script = document.createElement('a');
script.setAttribute('href',
baseUrl+"/highStarHotel/download/importEmployeeTemplates/v1?
token="+Cookie.get("HOTEL-MA-TOKEN"));
script.setAttribute('target', '_blank');
script.setAttribute("download", "批量导入酒店员工信息模块.xlsx");
document.body.appendChild(script);
script.click();
document.body.removeChild(script);
},
2、使用iframe 标签下载(批量下载下载)
downModel() {
// this.downUrlMenu 批量下载链接
_.each(this.downUrlMenu, (col) => {
let iframe = document.createElement("iframe");
iframe.setAttribute('style','display:none;height:0')
iframe.setAttribute('src',col)
document.body.appendChild(iframe);
setTimeout(()=>{
document.body.removeChild(iframe);
},2000)
})
}
3、使用form表单下载
/**
* 导出到本地
*
*/
createSelectedForm(url,params) {
let form = $("<form/>")
form.attr("target", "_blank").attr("method", "get").attr("action", url)
_.each(params, (value, key) => {
value = value ? value : ""
form.append("<input type='hidden' value='"+value+"' name='"+key+"' />")
})
form.appendTo(document.body)
form.submit()
form.remove()
}
二、 监听下载完成实现
实现原理:通过XMLHttpRequest发送请求
- 解决不同浏览器的下载触发navigator.msSaveBlob
- 利用FileReader来读取文件内容,关键是设置a标签,将blob的文件内容转换为base64并放入a标签的href中,模拟点击来进行下载。
- 对于非常小的文件,比如小图标等可以用base64作为url地址,对于其他的大文件,可以将文件存入内存中createObjectURL,用虚拟内存地址指向。
URL.createObjectURL 将blob形式的文件存入并返回一个url以供下载。
而URL.revokeObjectURL则将该内存释放。 - 至此,可以看到大文件根本不能使用base64,直接使用blob是最好的
- Content-Type 需与下载指定类型一致,否则乱码;https://www.runoob.com/http/http-content-type.html
exportExcel(type) {
let _that = this,
downWord =''公区查验(复验)报告.docx',
url = "/ydyf_stat/v1/fb/ci_type_stat/export_issue_report/",
params = {
type:type
},
xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type"
, "application/x-www-form-urlencoded"); // 下载文件类型 .docx
xhr.responseType = "blob"; // 返回类型blob
// 定义请求完成的处理函数
xhr.onload = function () {
// 请求完成 // 返回200
if (this.status === 200) {
let blob = this.response;
try {
let jsonData = JSON.parse(blob);
if (jsonData.code) {
// 说明是普通对象数据,后台转换失败
// to do something
}
} catch (err) {
// 解析成对象失败,说明是正常的文件流
let fileName = decodeURIComponent(downWord);
if (navigator.msSaveBlob == null) {
let a = document.createElement('a');
a.download = fileName;
a.href = URL.createObjectURL(blob);
$("body").append(a); // 修复firefox中无法触发click
a.click();
URL.revokeObjectURL(a.href);
$(a).remove();
} else {
navigator.msSaveBlob(blob, fileName);
}
}
}
};
// 发送ajax请求
xhr.send(qs.stringify(params))
},
注意
- axios设置responseType=blob导出文件和失败返回json处理
axios设置instance.defaults.responseType = 'blob’请求下载导出一个文件,请求成功时返回的是一个流形式的文件,正常导出文件。但是请求失败的时候返回的是json ,不会处理错误信息,而是直接导出包含错误信息的文件。
可以通过返回的blob数据type类型进行区分,如果type是文件类型,导出文件,如果type是json则把blob数据转为string,处理错误信息。
三、 后端设置
-
后端设置下载请求的响应头 Content-Disposition 强制下载
这是最通用的一种方式 不受跨域和请求方式的影响Content-Disposition: attachment; filename="filename.jpg"
想使用window.open实现强制下载的可以用这种方式
在常规的 HTTP 应答中,该响应头的值表示对响应内容的展现形式
- inline 表示将响应内容作为页面的一部分进行展示
- attachment 表示将响应内容作为附件下载,大多数浏览器会呈现一个“保存为”的对话框
- filename(可选) 指定为保存框中预填的文件名