Web页面中文件下载的几种方式
浏览器直接接管
这种方式通常通过创建一个a
标签来,将需要下载的文件链接放到它的href
属性上,设置download
属性让浏览器将链接的 URL 视为下载资源。
js
const fileUrl = 'http://eg.com/file.zip';
// 创建一个隐藏的可下载链接
const downloadLink = document.createElement('a');
downloadLink.style.display = 'none';
downloadLink.download = 'file.zip';
// 不需要将它嵌入页面
downloadLink.click();
优点:
- 代码量少,简洁。
- 浏览器接管下载过程,随时可以暂停、继续。
缺点:
- 只支持
GET
请求的链接。 - 带权限的链接需要添加权限验证信息到链接上。
- 图片等资源的链接需要在服务端接口设置该链接的
Content-Disposition
为attachment
,而非inline
。不然会直接在浏览器上打开。
xhr或fetch获取文件
这种方式属于先调用接口,再保存。保存的方式分为创建URL对象使用浏览器下载器保存和调用现在浏览器接口直接保存
这种方式同样支持分片下载,不过不同的是无论是什么保存方式,都无法暂停后继续保存后续分片到当前文件中。
分片下载的方式有两种:
- 接口参数,例如下载链接为:
http://eg.com/file.zip?patch=1
,通过传递参数告诉接口需要下载的是哪块分片,类似m3u8视频原理。不过在下载文件中不建议使用。 - 请求头Range,原理相同,方式不同,这种方式预先发起一个head请求,得到待下载文件的大小(Content-Length),然后循环发起分片请求并设置请求头
Range: bytes=${start}-${end}
,告诉服务器需要下载那部分内容,控制权在客户端。
创建URL对象保存
这种保存方式需要整个文件下载完成后才能保存,分布分片都无所谓了。
js
fetch(fileUrl)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
// 创建一个URL表示这个blob对象
const url = window.URL.createObjectURL(blob);
downloadLink.href = url;
downloadLink.download = 'file.zip';
downloadLink.click();
// 释放创建的URL对象
window.URL.revokeObjectURL(url);
})
.catch(error => {
console.error('下载文件时出错:', error);
});
优点:
- 可以使用项目封装的接口请求工具,不限制请求方式,不限制权限。
缺点:
- 需要将整个文件保存在内存中。
showSaveFilePicker
js
const fileHandle = await window.showSaveFilePicker({
suggestedName: 'file.zip'
});
const writableStream = await fileHandle.createWritable();
// 分片
const response = await fetch(fileUrl, {
headers: {
Range: `bytes=${start}-${end}`
}
});
if ((response.ok || response.status === 206) && response.body) {
const reader = response.body.getReader();
let done = false;
while (!done && !pauseRequested && !cancelRequested) {
const { value, done: chunkDone } = await reader.read();
if (value) {
await writableStream.write(value);
downloadedSize += value.length;
}
done = chunkDone;
}
}
优点:
- 不必将整个文件存在内存中,分片下载每片后,即可实时写入目标文件。
缺点:
-
浏览器兼容极差