背景
当我们将图片,视频,音乐,PDF或者其他文件上传给后端时,后端为了文件名不重复,可能会做一些处理。比如使用日期+时间戳+随机数的方式名命,又或者使用UUID或其他方式生成文件名。这就会导致一个问题。用户下载文件后,不能直观的通过文件名判断文件内容。举个例子:
- 夜曲 – 周杰伦.mp3 => 6fe3c93a-d3cd-47c6-ac54-11a6978a08cf.mp3
- xxx简历.pdf => a4e50bfc-63b0-4a19-b0c9-363c7467b2b4.pdf
- 全家福.jpg => ad58b4d1-022c-4095-aa21-829cda8f9f33.jpg
上述列子还可以通过后缀名区分,但如果你下载的文件都是同一类型的,那…
下载文件并重命名
对于浏览器来说,当你点击的链接是跳转到一张图片,一个视频,一首音乐的时候,浏览器会直接在新标签页打开并预览,如果你想下载这些文件,你需要右键另存为,或者找到下载按钮。对于压缩包,或其他无法预览的文件,则会直接下载。那有没有办法跳过右键另存为,或找下载按钮这些步骤,直接下载呢?大家应该都试过给a标签添加download属性这种方式吧。
<a href="path/ad58b4d1-022c-4095-aa21-829cda8f9f33.jpg" download="全家福">download</a>
复制代码
很遗憾,大多数情况下并没有任何效果。那到底该如何实现呢?其实很简单,需要涉及到XMLHttpRequest,URL.createObjectURL()。
实现
纸上得来终觉浅,绝知此事要躬行。
话不多说,直接上代码:
<button id="btn" type="button">下载</button>
const btn = document.getElementById('btn');
btn.addEventListener('click', function () {
downloadFile('path/ad58b4d1-022c-4095-aa21-829cda8f9f33.jpg', '全家福');
});
/**
* 下载文件
* @param {string} url
* @param {string} fileName
*/
function downloadFile(url, fileName) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// 响应类型设置为blob
xhr.responseType = 'blob';
// 请求成功
xhr.addEventListener('load', function () {
if (xhr.status === 200) {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(xhr.response);
a.download = fileName;
// 将a标签添加到body中是为了更好的兼容性,谷歌浏览器可以不用添加
document.body.appendChild(a);
a.click();
// 移除
a.remove();
// 释放url
window.URL.revokeObjectURL(a.href);
}
});
// 监听下载进度
xhr.addEventListener('progress', function (e) {
let percent = Math.trunc(e.loaded / e.total * 100);
});
// 错误处理
xhr.addEventListener('error', function (e) {
// todo
});
// 发送请求
xhr.send();
}
复制代码
效果图
在线Demo:jsdemo.codeman.top/html/downlo…
缺点
当然此方法并不是完美的,存在以下问题。
- 非同源存在跨域问题(Demo已设置允许资源跨域)
- 会预先加载文件,如果文件比较大,网速较慢,服务器带宽比较低时,等待时间会比较长(所以Demo弄了个进度条)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END