前端点击下载的实现方式
下载功能是前端经常要实现的一种方法,下面整理了集中常用的前端下载方式,仅供参考!!!
一、window.open()
window.open()方法一般用于打开一个新页面或新的标签页,本身并不提供下载服务,是否触发下载或打开新页面取决于所提供的 URL 以及服务器端如何配置响应头。
当你提供的是一个只想网页资源的URL(例如 HTML 页面、图像、视频等),并且服务器正确地设置了 Content-Type
响应头来表示这是一个可以显示在浏览器中的内容类型时,window.open
会尝试在新的浏览器窗口或标签页中加载并显示该网页。如果服务器返回的状态码是成功的(如 200 OK),并且 Content-Type
是浏览器支持的内容类型(如 text/html
, image/jpeg
等),则浏览器会尝试渲染这些内容。
如果服务器发送了带有 Content-Disposition: attachment
的 HTTP 响应头,无论 URL 指向什么类型的文件,浏览器都会提示用户下载该文件而不是直接在浏览器中打开它。即使没有显式设置 Content-Disposition
头,如果文件类型不是浏览器通常会内联显示的类型(如 .exe
, .zip
, .pdf
等),一些浏览器可能会默认选择下载该文件。对于某些特定文件类型,即使它们理论上可以在浏览器中查看(如 PDF 文件),但如果用户偏好设置为下载此类文件,或者浏览器插件/扩展影响了默认行为,也会导致下载行为。
二、直接使用<a>标签下载
<a>
标签的href
和它的 download
属性可以指定一个链接为下载链接并设置所下载文件的名字。用户点击该链接时,浏览器会尝试下载链接指向的文件而不是导航到该链接。
download
属性指示浏览器该下载而不是打开该文件,同时该属性值即下载时的文件名
例:
<a href="path/to/file.pdf" download="filename.pdf">Download</a>
但这个方法对跨域资源可能有限制,并且不支持所有类型的文件,它适用于已经存在于服务器上的文件,或者是通过服务器端生成并返回给客户端的文件。它依赖于服务器正确地提供文件内容,并且通常需要正确的 MIME 类型和 HTTP 响应头(如 Content-Disposition: attachment
)来触发下载。
如果遇到使用<a>
标签无法正常下载的情况,需排查以下问题:
-
- 服务器端响应头:
Content-Disposition: attachment
响应头,它告诉浏览器这个文件应该被下载而不是直接显示-
-
MIME 类型:
-
服务器返回的 Content-Type
响应头应该匹配文件的实际类型。对于二进制文件或非文本文件,确保 MIME 类型是正确的,比如 PDF 文件应该是 application/pdf
。
-
-
文件路径和存在性:
-
确认 href
属性中的 URL 指向的是一个实际存在的文件,并且路径是正确的。如果路径不正确或文件不存在,浏览器可能无法找到资源,导致无法下载。
-
-
跨域问题:
-
如果文件位于不同的域名下,可能会遇到跨域限制。在这种情况下,download
属性可能不起作用。你需要确保目标服务器允许来自你网站的跨域请求(CORS),或者考虑将文件托管在同一域名下。
-
-
浏览器支持和限制:
-
不同的浏览器对 <a download> 的支持程度不同,尤其是旧版本的浏览器可能不完全支持此属性。此外,一些浏览器对某些类型的文件(如 .html, .htm)有特殊处理,即使设置了 download 属性,它们也可能不会触发下载行为。
三、blob下载
当你需要从 JavaScript 代码中创建或处理文件内容时,可以使用 Blob
对象来表示文件数据,然后使用 URL.createObjectURL
方法生成一个临时的 URL 来触发下载。
使用Blob
对象来进行下载大体有两种情况,其一为通过自定义数据创建Blob
对象,其二为通过请求内容创建Blob
对象
示例如下:
1、自定义数据创建Blob
对象
const downloadFile = () => { const blob = new Blob([data], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = 'filename.pdf'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); // 清理内存 }
2、通过请求内容创建Blob
对象
// fetch方法内置了blob()方法,可以直接将返回内容创建为blob对象,其余方式须手动创建 fetch(fileUrl) .then(res => res.blob()) .then(blob => { const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) }) // 其余方法:以axios为例 axios({ url: '/api/download', method: 'GET', responseType: 'arraybuffer', // 或者 'blob' 如果支持 }) .then(response => { const blob = new Blob([response.data], { type: response.headers['content-type'] }); // 使用 blob const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) });
四、实际案例
当下载图片为后端返回的图片链接,且图片跨域时,我们需要将上述方法进行拼装使用
首先,我们需要利用canvas将图片链接绘制成一个新的图片,并将图片的跨域属性修改为匿名,然后再通过blob的方式进行下载
例:
const download = (link) => { // 创建新的图片对象 const img = new Image() // 将链接赋给新的图片对象 img.src = link // 设置图片跨域属性为匿名 img.setAttribute('crossOrigin', 'Anonymous') // 图片加载完成后执行回调函数 img.onload = function () { // 创建一个新的画布元素 const canvas = document.createElement('canvas') // 获取画布的2D上下文 const context = canvas.getContext('2d') // 判断是否成功获取到画布上下文 if (context) { // 设置画布的宽度和高度与图片一致 canvas.width = img.width canvas.height = img.height // 在画布上绘制图片 context.drawImage(img, 0, 0, img.width, img.height) // 将画布内容转换为 Blob 对象 canvas.toBlob((blob) => { // 判断是否成功生成 Blob 对象 if (blob) { // 创建一个临时链接用于下载 const url = URL.createObjectURL(blob) const a = document.createElement('a') const event = new MouseEvent('click') // 设置下载的文件名 a.download = 'default.png' // 设置链接地址为 Blob 对象的 URL a.href = url // 模拟点击事件触发下载 a.dispatchEvent(event) // 释放 Blob 对象的 URL URL.revokeObjectURL(url) } else { console.error('生成 Blob 失败') } }) } else { console.error('获取 Canvas 上下文失败') } } }
五、名词讲解
1、blob
Blob
(Binary Large Object)是 JavaScript 中表示不可变的、原始数据的类文件对象。它用于处理二进制数据,如图像、音频、视频文件等,并且可以用来构建文件或作为网络请求中的响应体。Blob
对象提供了对文件和二进制数据的访问和操作能力,而不需要将这些数据转换为字符串或其他格式。
blob提供的方法和用途
1. slice()
-
-
- 描述:创建一个新的
Blob
对象,其内容是原Blob
的一个子集。 - 语法:
- 描述:创建一个新的
-
const newBlob = blob.slice([start[, end[, contentType]]]);
-
-
- 参数:
start
(可选):开始位置的字节偏移量,默认为 0。end
(可选):结束位置的字节偏移量,默认为blob.size
。contentType
(可选):新Blob
的 MIME 类型。
- 返回值:一个新的
Blob
对象。
- 参数:
-
示例:
const blob = new Blob(['Hello, world!'], { type: 'text/plain' }); const slicedBlob = blob.slice(0, 5); // 创建包含 "Hello" 的新 Blob
2. stream()
-
-
- 描述:返回一个
ReadableStream
,用于读取Blob
的内容。这在处理大文件时特别有用,因为它允许你逐步读取数据而不是一次性加载整个文件到内存中。 - 语法:
- 描述:返回一个
-
const stream = blob.stream();
-
-
- 返回值:一个
ReadableStream
对象。
- 返回值:一个
-
示例:
const blob = new Blob(['large file content...']); const reader = blob.stream().getReader(); reader.read().then(({ done, value }) => { if (done) { console.log('Reading completed'); } else { console.log(`Received ${value.length} bytes of data`); } });
3. arrayBuffer()
-
-
- 描述:将
Blob
的内容解析为ArrayBuffer
。这对于需要直接操作二进制数据的应用非常有用。 - 语法:
- 描述:将
-
blob.arrayBuffer().then(arrayBuffer => { // 处理 arrayBuffer });
-
-
- 返回值:一个
Promise
,它在Blob
内容被完全读取并转换为ArrayBuffer
后解决。
- 返回值:一个
-
示例:
const blob = new Blob(['some binary data...']); blob.arrayBuffer().then(buffer => { const uint8Array = new Uint8Array(buffer); console.log(uint8Array); });
4. text()
-
-
- 描述:将
Blob
的内容解析为文本字符串。这适用于Blob
包含纯文本数据的情况。 - 语法:
- 描述:将
-
blob.text().then(text => { // 处理 text });
-
-
- 返回值:一个
Promise
,它在Blob
内容被完全读取并转换为文本后解决。
- 返回值:一个
-
示例:
const blob = new Blob(['Hello, world!'], { type: 'text/plain' }); blob.text().then(text => { console.log(text); // 输出 "Hello, world!" });
2、URL.createObjectURL
URL.createObjectURL
是 JavaScript 中用于创建一个指向 Blob
或 File
对象的 URL 的方法。这个 URL 可以被用作 <img>
标签的 src
属性、<a>
标签的 href
属性等,从而允许在页面中直接显示或下载这些对象的内容。这是一个非常有用的方法,特别是在处理文件上传、下载和多媒体内容时。
主要用途:
-
-
- 显示多媒体内容:可以将图片、音频、视频等内容通过
Blob
或File
对象直接显示在页面上。 - 提供文件下载链接:可以为动态生成的文件或从服务器获取的文件创建临时下载链接。
- 避免跨域问题:对于某些情况下,使用
Blob
URL 可以绕过跨域资源加载的问题。
- 显示多媒体内容:可以将图片、音频、视频等内容通过
-
3、URL.revokeObjectURL
用于撤销之前通过 URL.createObjectURL()
方法创建的对象 URL。撤销对象 URL 的主要目的是释放与该 URL 相关联的资源,避免内存泄漏。
当你创建一个对象 URL 时,浏览器会为这个 URL 分配一定的内存来存储文件或二进制数据(例如图片、视频等)。如果你不再需要这些资源,但没有显式地释放它们,那么即使页面上不再引用这些 URL,浏览器仍然会保持这些资源的分配,直到页面关闭。这可能会导致不必要的内存占用,尤其是在频繁创建和销毁对象 URL 的情况下。
在循环中创建多个对象 URL 或者在用户交互后创建了临时使用的对象 URL时,要尽可能早地调用 URL.revokeObjectURL
来释放不再需要的资源。而且URL.revokeObjectURL
不会对传入的参数进行有效性检查,所以在调用此方法前最好确认传入的是有效的对象 URL,并且是通过 URL.createObjectURL
创建的。
本文作者:草丛莽撞人
本文链接:https://www.cnblogs.com/tagzeee/p/18636069
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步