浏览器打开链接后默认会尝试预览该文件,浏览器下载的本质是告诉浏览器将该资源提供给用户进行保存
两种情况浏览器会主动将一个链接对应的资源进行下载
- 响应
Content-Disposition为attachment显示的告诉浏览器进行资源的下载 - 响应
Content-Type为浏览器不支持预览的类型/缺少该字段但浏览器判断出该文件不支持预览
手动实现文件下载需要利用浏览器的这种行为(本质上就是打开文件的下载链接),可以有几种方式:
-
form表单提交- 一种古老的文件下载方式
- 核心是当需要进行下载时动态生成一个表单,利用表单提交的功能来实现文件的下载
function downloadFile (url) { const form = document.createElement('form') form.action = url form.method = 'get' form.style.display = 'none' document.body.appendChild(form) form.submit(); // 提交表单,下载文件 document.body.removeChild(form) } -
window.open/location.href -
a链接跳转
这几种方式都可以让页面跳转到下载链接实现文件的下载,并且可以发现,如果是在当前 tab 页面跳转触发下载行为,地址栏不会改变 缺点这几种方式当然也一样:
- 当跳转的下载链接缺少
Content-Disposition时只能下载浏览器无法预览的文件类型,浏览器可预览的文件类型会跳转为预览 - 无法携带多余的 HTTP header,在一些鉴权的场景无法使用
a 标签 download
HTML5 新增download属性可以让浏览器直接下载所有类型文件,无需依赖Content-Disposition
<a href="example.jpg" download>点击下载</a>
<!-- 重命名下载文件 -->
<a href="example.jpg" download="test">点击下载</a>
download仅对同源 URL 或blob:、data:协议起作用
- 对于跨域链接
download会失效,浏览器依旧会优先进行文件的预览而不是下载 - 如果非要进行跨域下载,可以先将数据转换为
blob:或data:再进行下载
blob 和 dataURL
对于blob: 或data:协议的下载
- 获取二进制数据并转化为
Blob,使用URL.createObjectUrl生成地址 / 获取二进制数据并转化为 Data URL - 借助
a标签进行下载
function downloadFile(url, name) {
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = name;
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
}
function fetchData() {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.open('get', path)
xhr.responseType = 'blob'
xhr.send()
xhr.onload = function () {
resolve(this.response)
};
})
}
// blob
const response = await fetchData()
const url = URL.createObjectURL(this.response)
downloadFile(url, 'example.txt')
URL.revokeObjectURL(url)
// data url
const response = await fetchData()
const fileReader = new FileReader()
fileReader.readAsDataURL(response)
fileReader.onload = function () {
downloadFile(this.result, 'example.txt')
}
这两种方式存在的问题也都一样
- 需要先将文件请求到本次才进行下载,具有两次下载
- 大文件会占用大量内存,这可能导致性能问题,尤其是在移动设备或资源受限的环境中
优点是
- 可以在客户端动态生成文件内容,不一定需要再服务器上生成或存储文件
- 可以在需要鉴权的环境下进行文件的下载
Content-Disposition
Content-DispositionHTTP 响应头指示客户端以何种形式显示响应内容
基本的格式:
Content-Disposition: <disposition-type>; [filename=<filename>]
disposition-type表示处理方式
attachment: 告诉客户端应该将文件作为附件下载,如果后面存在filename,会作为下载的文件名inline: 指明直接展示资源,当然当浏览器不支持改类型的资源是依旧会提示下载- 其他自定义值: 可以是其他处理方式,具体效果取决于用户代理的支持情况
优先级
attachiment的优先级是最高的,无论使用何种方式跳转链接,都会实现文件下载。
当 disposition-type值为inline时,download 会优先于 inline实现文件下载
参考
- 你真的会用标签下载文件吗?
- 前端下载文件的5种方法的对比(附加获取文件名
- ChatGPT