前端点击下载的实现方式

下载功能是前端经常要实现的一种方法,下面整理了集中常用的前端下载方式,仅供参考!!!

一、window.open()

window.open()方法一般用于打开一个新页面或新的标签页,本身并不提供下载服务,是否触发下载或打开新页面取决于所提供的 URL 以及服务器端如何配置响应头。

当你提供的是一个只想网页资源的URL(例如 HTML 页面、图像、视频等),并且服务器正确地设置了 Content-Type 响应头来表示这是一个可以显示在浏览器中的内容类型时,window.open 会尝试在新的浏览器窗口或标签页中加载并显示该网页。如果服务器返回的状态码是成功的(如 200 OK),并且 Content-Type 是浏览器支持的内容类型(如 text/htmlimage/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> 标签无法正常下载的情况,需排查以下问题:

    • 服务器端响应头
      确保服务器在响应中包含适当的 HTTP 头来指示浏览器这是一个应该被下载的文件。特别是 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 中用于创建一个指向 BlobFile 对象的 URL 的方法。这个 URL 可以被用作 <img> 标签的 src 属性、<a> 标签的 href 属性等,从而允许在页面中直接显示或下载这些对象的内容。这是一个非常有用的方法,特别是在处理文件上传、下载和多媒体内容时。

主要用途:

      1. 显示多媒体内容:可以将图片、音频、视频等内容通过 Blob 或 File 对象直接显示在页面上。
      2. 提供文件下载链接:可以为动态生成的文件或从服务器获取的文件创建临时下载链接。
      3. 避免跨域问题:对于某些情况下,使用 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 中国大陆许可协议进行许可。

posted @   草丛莽撞人  阅读(172)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起