一、创建一个隐藏的 <a>
标签,并模拟点击事件来触发文件下载
function downloadTaskInfo(url) {
var a = document.createElement('a');
a.href = url;
if (document.all) {
a.click();
} else {
var e = document.createEvent("MouseEvents");
e.initEvent("click", true, true);
a.dispatchEvent(e);
}
}
downloadTaskInfo(downloadUrl);
优点:
- 实现简单,不需要额外的 JavaScript 库。
- 可以在大多数现代浏览器中工作(除了某些情况下需要使用
document.execCommand('download')
)。 - 文件下载进度会体现在浏览器的文件下载列表中,对于大文件下载不需要做下载进度的展示功能。
缺点:
- 如果服务器返回的内容不适合直接下载(例如,不是常见的文件类型),可能不会触发下载对话框。
- 对于一些旧版的 IE 浏览器(如 IE11),它使用了
document.all
来兼容,但这并不是一种标准的方法,并且可能在某些情况下不起作用。 - 没有错误处理机制,如果 URL 无效或服务器端出现问题,则下载会失败。
- 文件名包含中文时,下载后会乱码,需要对文件名进行URLEncode。
String fileName = URLEncoder.encode(targetFileName);
File file = TempFileService.createTempFile(targetFileName + ".zip");
二、先通过 AJAX 请求获取 Blob 对象,然后创建一个临时的下载链接来触发下载
function getBlob(url) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
};
xhr.send();
});
}
/**
* 保存 blob
* filename 想要保存的文件名称
*/
function saveAs(blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement('a');
const body = document.querySelector('body');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// fix Firefox
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
}
/**
* 下载
* @param {String} url 目标文件地址
* @param {String} filename 想要保存的文件名称
*/
function download(url, filename) {
getBlob(url).then(blob => {
saveAs(blob, filename);
});
}
优点:
- 使用了 XMLHttpRequest 和 Blob API,这使得可以处理更广泛的文件类型,并且可以控制下载的内容类型。
- 提供了更精细的错误处理,可以通过检查 XHR 的状态码来确定请求是否成功。
- 支持指定文件名,这对于保持文件组织是有帮助的。
- 更符合现代 Web API 的使用方式。
缺点:
- 实现稍微复杂一些,因为涉及到 Promise 和异步操作。
- 需要确保服务器正确设置了响应头(如
Content-Type
和Content-Disposition
),以便 Blob 能够正确地表示为可下载的文件。 - 请求全部完成后,文件才会呈现到浏览器的下载列表中,对于大文件下载需要做下载进度查看的功能才能保证较好的用户体验。
总结
第一种方法适合快速简单的下载需求,而第二种方法提供了更多的灵活性和错误处理能力,更适合需要更细致控制的应用场景。如果你的应用需要支持多种文件类型,并且需要更好的用户体验(比如自定义文件名),那么第二种方法可能是更好的选择。