盘点前端实现文件下载的几种方式
前端涉及到的文件下载还是很多应用场景的,那么前端文件下载有多少种方式呢?每种方式有什么优缺点呢?下面就来一一介绍。
1. 使用 a 标签下载
通过a
标签的download
属性来实现文件下载,这种方式是最简单的,也是我们比较常用的方式,先来看示例代码:
<a href="http://www.baidu.com" download="baidu.html">下载</a>
就上面的这个示例,我们点击下载,发现是跳转到了百度的首页,并没有真的下载文件。
因为a
标签下载只能下载同源
的文件;如果是跨域
的文件,比如图片、音视频等媒体文件等都无法使用上面的a
标签方式下载。
上面的代码是直接通过书写a
标签来实现文件下载;我们也可以通过js
来实现,代码如下:
const a = document.createElement('a') a.href = 'http://www.baidu.com' a.download = 'baidu.html' a.click()
效果和上面的一样,都是跳转到百度的首页,没有下载文件。
这里的重点是a
标签的download
属性,这个属性是HTML5
新增的。
它的作用是指定下载的文件名,如果不指定,那么下载的文件名就会根据请求内容的Content-Disposition
来确定,如果没有Content-Disposition
,那么就会使用请求的URL
的最后一部分作为文件名。
2. 使用 window.open 下载
上面使用a
标签的案例也可以通过window.open
来实现,效果是一样的,代码如下:
window.open('http://www.baidu.com', '_blank')
这里的_blank
是指定用浏览器新窗口打开链接;如果不指定,那么就会在当前页面打开。
同样a
标签的download
属性也是可以使用的,代码如下:
window.open('http://www.baidu.com', '_blank', 'download=baidu.html')
当然这种方式也是有缺陷的,对比于a
标签,window.open
方式不能下载.html
、.htm
、.xml
、.xhtml
等文件;因为这些文件会被当成html
文件来处理,所以会直接在当前页面打开。
同样也不能下载跨域
的文件,这个是window.open
实现下载原理决定的。
3. 使用 location.? 下载
以下可以实现页面跳转的属性,都可以实现文件下载
3.1 location.href
// 这个方式和window.open是一样的 location.href = 'http://www.baidu.com'
这种方式拥有window.open
的所有缺陷,所以不推荐使用,这里只当作了解,所以不做过多的讲解。
3.2 location.assign
location.assign('http://www.baidu.com')
3.3 location.replace
location.replace('http://www.baidu.com')
3.4 location.reload
location.reload('http://www.baidu.com')
location.reload
是有点特殊的,它的作用是重新加载当前页面,但是它也可以接受一个参数,这个参数就是要跳转的页面,所以也可以实现文件下载。
当然同location.href
一样,这些方式的缺点都一样,同时还有属于每个属性自身的特性,这里只当拓展知识,不做过多的讲解。
4. XMLHttpRequest
这种方式就是我们常说的ajax
下载,包括Axios
、Fetch
等,代码如下:
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://www.baidu.com')
xhr.send()
xhr.onload = function () {
const blob = new Blob([xhr.response], { type: 'text/html' })
const a = document.createElement('a')
const url = URL.createObjectURL(blob)
a.href = url
a.download = 'baidu.html'
a.click()
// 删除映射,释放内存
URL.revokeObjectURL(url);
}
这里关于XMLHttpRequest
相关的知识就不做展开了,只讲和文件下载相关的部分。
上面代码主要的逻辑是当我们的请求成功后,我们会拿到响应体Response
,这个Response
就是我们要下载的内容。
然后我们把它转换成Blob
对象,通过URL.createObjectURL
来创建一个URL
,最后使用a
标签的download
属性来实现文件下载。
不过它有个副作用。虽然这里有 Blob
的映射,但 Blob
本身只保存在内存中的。浏览器无法释放它。
在文档退出时(unload),该映射会被自动清除,因此 Blob
也相应被释放了。但是,如果应用程序寿命很长,那这个释放就不会很快发生。
因此,如果我们创建一个 URL,那么即使我们不再需要该 Blob
了,它也会被挂在内存中。
不过,URL.revokeObjectURL 可以从内部映射中移除引用,允许 Blob
被删除并释放内存。所以,在即时下载完资源后,不要忘记立即调用 URL.revokeObjectURL。
p.s.1 Blob 对象
下面是MDN
对Blob
对象的定义:
Blob
对象表示一个不可变、原始数据的类文件对象。
Blob
的数据可以按文本或二进制的格式进行读取,也可以转换成ReadableStream
来用于数据操作。
Blob
表示的不一定是JavaScript
原生格式的数据。
File
接口基于Blob
,继承了Blob
的功能并将其扩展以支持用户系统上的文件。
Blob
对象是html5
新增的对象,它的作用是用来存储二进制数据的,比如图片、视频、音频等,它的使用方法如下:
/** * @param {Array} array 二进制数据 * @param {Object} options 配置项 * @param {String} options.type 文件类型,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。 * @param {String} options.endings 用于指定包含行结束符\n的字符串如何被写入。默认为transparent,表示不会修改行结束符。还可以指定为native,表示会将\n转换为\r\n。 */ const blob = new Blob([], { type: '' })
Tips:需要关注的是type
属性,默认情况下, Blob
对象是没有type
属性的,那么这个Blob
就是一个无类型的Blob
,文件不会损毁,但是无法被正常识别。
p.s.2 URL.createObjectURL
下面是MDN
对 URL.createObjectURL
方法的定义:
URL.createObjectURL()
静态方法会创建一个DOMString
,其中包含一个表示参数中给出的对象的URL
。
这个URL
的生命周期和创建它的窗口中的document
绑定。
这个新的URL
对象表示指定的File
对象或Blob
对象。
这个方法是用来创建一个URL
的,它的作用是把一个Blob
对象转换成一个URL
,这个URL
可以用来下载文件,也可以用来预览文件,代码如下:
const url = URL.createObjectURL(blob)
这里需要注意的是,这个URL
的生命周期和创建它的窗口中的document
绑定。
也就是说,当我们的document
被销毁后,这个URL
就会失效,所以我们需要在合适的时机销毁它。
代码如下:
URL.revokeObjectURL(url)
回到我们刚才下载的问题,我们是通过Blob
对象来解决,但是我们的type
属性是写死的,如果在文件类型是确定的情况下是没问题的。
但是如果这个接口就是下载文件的接口,文件可能是各种类型的,我们应该怎么处理?
这里的没有正确答案,第一个可以和接口提供者进行协商,协商方案是不确定的;第二就是通过Response
的header
来获取文件的type
,也是我们要讲的:
const type = response.headers['content-type'] const blob = new Blob([response.data], { type })
这里我们通过Response
的header
来获取type
,然后再创建Blob
对象,这样就可以正确的下载文件了。
其实content-type
也可能是application/octet-stream
,这个时候我们就需要通过file-type
来获取文件的type
了。
下面的代码是通过file-type
来获取文件的type
:
import {fileTypeFromStream} from 'file-type'; const type = await fileTypeFromStream(response.body); const blob = new Blob([response.data], { type })
5. 总结
上面的方案这么多,其实最终还是落到a
标签上,所以不管是通过浏览器的内置行为进行下载,还是通过ajax
进行下载,文件下载的最终还是浏览器的行为。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-07-12 【fiddler】配置代理后个别app连不上网的问题
2021-07-12 使用Fiddler域名过滤、断点、小技巧绕过前端验证
2021-07-12 App上架各大应用市场的地址及操作方法
2021-07-12 获取APK获取APK证书MD5、SHA1、SHA256等秘钥