前端文件有多种下载方式,首先,明确一个基本的概念。出于安全方面的考虑,浏览器中运行的Js代码是不可以“直接”对本地磁盘进行读写的,这个是浏览器的js引擎所限制的。但是由于浏览器本身作为一个应用程序,它是可以操作文件系统的。所以前端实现下载功能一定是依赖于浏览器的特性的,而不是js的特性。参考链接
a标签下载
<a download="文件名" href="文件下载接口地址">下载</a>
download的属性是HTML5新增的属性
href属性的地址必须是非跨域的地址,如果引用的是第三方的网站或者说是前后端分离的项目(调用后台的接口),这时download就会不起作用。
此时,如果是下载浏览器无法解析的文件,例如.exe,.xlsx..那么浏览器会自动下载,但是如果使用浏览器可以解析的文件,比如.txt,.png,.pdf....浏览器就会采取预览模式
所以,对于.txt,.png,.pdf等的预览功能我们就可以直接不设置download属性(前提是后端响应头的Content-Type: application/octet-stream,如果为application/pdf浏览器则会判断文件为 pdf ,自动执行预览的策略)
我们也可以通过click事件触发function动态的创建a标签来下载文件
export const downloadFile = (url, fileName?) => {
const dwldLink = document.createElement('a');
dwldLink.setAttribute('href', url);
dwldLink.style.visibility = 'hidden';
if(fileName) dwldLink.download = fileName;
document.body.appendChild(dwldLink);
dwldLink.click();
// remove it after click
document.body.removeChild(dwldLink);
}
url上设置response-content-disposition属性
在下载url后添加?response-content-disposition=attachment;filename=文件名,通过添加这个参数,服务器将识别该请求头,将响应内容作为文件进行下载,并使用指定的文件名命名下载的文件。
response-content-disposition=attachment:表示将服务器的响应内容以附件形式下载,而不是直接在浏览器中打开。filename=文件名:指定下载的文件名,可以自定义文件名,确保用户下载时看到的文件名符合预期。
优点
- 可以下载txt、png、pdf等类型文件
- 下载文件将由浏览器管理,无需做额外处理
缺点
- a标签只能做get请求,所有url有长度限制,并且无法携带body
- 无法获取下载进度
- 无法在header中携带token做鉴权操作
- 无法判断接口是否返回成功
form标签下载
function inputDownloadEvt(method, url, paramsKey, paramsValue) {
const form = document.createElement('form');
form.style.display = 'none';
form.setAttribute('target', '_blank');
form.setAttribute('method', method);
form.setAttribute('action', url);
const input = document.createElement('input');
input.setAttribute('type','hidden');
// 对于get请求 最终会拼成http://192.168.66.183:13666/download?name=HAP.pdf
input.setAttribute('name', paramsKey);
input.setAttribute('value', paramsValue);
form.appendChild(input);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
优点
- 下载文件将由浏览器管理,无需做额外处理
- 兼容性好,不会出现URL长度限制问题
- form标签get和post都可以,可以携带body
缺点
- 无法获取下载进度
- 无法在header中携带token做鉴权操作
- 无法直接下载浏览器可直接预览的文件类型(txt、png、pdf会直接预览)
- 无法判断接口是否返回成功
window.open 与 location.href 下载
window.open(url, '_self');
window.location.href = url;
优点
- 简单方便直接
- 下载文件将由浏览器管理,无需做额外处理
缺点
- 会出现URL长度限制问题
- 需要注意url编码问题
- 无法获取下载进度
- 无法在header中携带token做鉴权操作
- 无法直接下载浏览器可直接预览的文件类型(txt、png、pdf会直接预览)
- 无法判断接口是否返回成功
http下载
const downloadTask = (url, fileName, token) => {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob"; // 声明返回类型为二进制数据
xhr.setRequestHeader("Authorization", token); // 设置自定义请求头
xhr.addEventListener("progress", function(event) {
if (event.lengthComputable) {
const p = (event.loaded / event.total) * 100;
// 设置下载进度
setDownloadPercent(p.toFixed())
}
});
xhr.onerror = onError;
xhr.onload = function() {
if (xhr.status === 200) {
setDownloadPercent(100)
const blob = new Blob([xhr.response], { type: "application/octet-stream" });
const downloadUrl = URL.createObjectURL(blob);
// 创建下载链接并点击触发下载
const link = document.createElement("a");
link.href = downloadUrl;
link.download = fileName;
link.click();
// 清理对象 URL
URL.revokeObjectURL(downloadUrl);
setTimeout(close, 500);
}
};
xhr.send();
}
前端获取到服务器端生成的字节流,此时数据是存在于js的内存中的,是不可以直接保存在本地的,利用Blob对象和window.URL.createObjectURL对象生成一个虚拟的URL地址,然后在利用浏览器的特性进行下载。 因此对于ajax下载大文件时,会出现浏览器崩溃情况,此时可以考虑使用链接直接下载或使用分片下载
优点
- 可以下载txt、png、pdf等类型文件
- 可以在header中携带token做鉴权操作
- 可以获取文件下载进度
- 可以判断接口是否返回成功
缺点
- 将后端返回的文件流全部获取后才会下载
- 下载文件过大,将会失败