前端 a链接 如何实现下载功能

news2024/11/20 21:24:04

目录

前言

标签 download

a 标签链接下载的实现

1. 整体流程

2. 实现步骤

3. 类图

4. 代码示例 

download 使用注意点

1. 同源 URL 的限制

2. 不能携带 Header

Blob 转换

方法1. 用作 URL(blob:)

方法2. 转换为 base64(data:)

两种方法总结与对比

responseType

扩展阅读

1. Blob

2. URL.createObjectURL()

3. URL.revokeObjectURL()

4. FileReader.readAsDataURL()


前言

在中后台项目中,前端难免需要处理下载的逻辑,需要下载的内容包括但不限于图片、Excel表格、CSV文件、MP4文件、PDF文件、TXT文件、JSON文件、HTML文件等等。虽然下载的内容各式各样,但是下载的原理大同小异。下面来一起学习一下前端是如何处理下载的。

<a> 标签 download

这应该是最常见,最受广大人民群众喜闻乐见的一种下载方式了,搭配上 download 属性, 就能让浏览器将链接的 URL 视为下载资源,而不是导航到该资源。

如果 download 再指定个 filename ,那么就可以在下载文件时,将其作为预填充的文件名。不过名字中的 / 和 \ 会被转化为下划线 _,而且文件系统可能会阻止文件名中的一些字符,因此浏览器会在必要时适当调整文件名。

a 标签链接下载的实现

1. 整体流程

为了实现通过a标签链接下载文件,我们需要对a标签的href属性进行设置,使其指向文件的下载链接。具体步骤如下表所示:

下面我们将逐步介绍每个步骤需要做的事情,并提供相应的代码和注释。

2. 实现步骤

步骤1:创建一个a标签元素
首先,我们需要使用document.createElement方法创建一个a标签元素,并将其赋值给一个变量,以便后续操作。

// 创建一个a标签元素
const downloadLink = document.createElement('a');

步骤2:设置a标签的href属性为文件的下载链接
接下来,我们需要将下载链接赋值给a标签的href属性,以便浏览器能够正确地下载文件。

// 设置a标签的href属性为下载链接
downloadLink.href = ''

步骤3:设置a标签的download属性,用于指定下载链接的默认文件名

如果我们想要指定下载链接的默认文件名,可以使用a标签的download属性。这样,当用户点击下载链接时,浏览器会自动将文件以指定的文件名保存到本地。

// 设置a标签的download属性为文件名
downloadLink.download = 'file.pdf'

步骤4:将a标签添加到HTML文档中

我们需要将创建的a标签元素添加到HTML文档中的某个元素中,以便用户能够看到下载链接并进行下载操作。

// 将a标签添加到HTML文档中的某个元素中
document.body.appendChild(downloadLink);

步骤5:触发a标签的点击事件,即模拟用户点击下载链接

为了触发文件的下载,我们需要模拟用户点击a标签的行为。可以使用click方法来触发a标签的点击事件。

// 触发a标签的点击事件,即模拟用户点击下载链接
downloadLink.click();

步骤6:完成文件下载

通过以上步骤,我们已经成功地实现了通过a标签链接下载文件的功能。用户点击下载链接后,浏览器会自动下载文件到本地。

3. 类图

下面是本文所涉及的类的关系示意图:

4. 代码示例 

// 创建一个a标签元素
const downloadLink = document.createElement('a');

// 设置a标签的href属性为下载链接
downloadLink.href = '

// 设置a标签的download属性为文件名
downloadLink.download = 'file.pdf';

// 将a标签添加到HTML文档中的某个元素中
document.body.appendChild(downloadLink);

// 触发a标签的点击事件,即模拟用户点击下载链接
downloadLink.click();

download 使用注意点

<a> 标签虽好,但还有一些值得注意的点:

1. 同源 URL 的限制

download 只在同源 URL 或 blob: 、 data: 协议起作用

也就是说跨域是下载不了的......(这种说法不全对,除非后端配置 Content-Disposition 为 attachment,后面会讲)

首先,非同源 URL 会进行导航操作。其次,如果非要下载,可以先将其转换为 blob: 或 data: 再进行下载

2. 不能携带 Header

使用 <a> 标签下载是带不了 Header 的,因此不能通过添加请求表头的形式来鉴权,但是可以将 sessionid 或 token 字段拼接到 URL 末尾来达到鉴权的目的。这里我们给出另一个解决方案:

  1. 先发送请求获取 blob 文件流,这样就能在请求时进行鉴权;
  2. 鉴权通过后再执行下载操作。

这样是不是就能很好的同时解决问题1和问题2带来的两个痛点了呢,而且下载的文件名也能自定义了

顺便提一下,location.href 和 window.open 也存在同样的问题。

Blob 转换

前文介绍到,在非同源请情况下可以将资源当成二进制的 blob 先拿到手,再进行 <a> 的下载处理。接下来,我们介绍两种 blob 的操作:

方法1. 用作 URL(blob:)

URL.createObjectURL 可以给 File 或 Blob 生成一个URL,形式为 blob:<origin>/<uuid>,此时浏览器内部就会为每个这样的 URL 存储一个 URL → Blob 的映射。因此,此类 URL 很短,但可以访问 Blob。

   // 下载 Excel 方法
  excel(data, fileName) {
    this.download0(data, fileName, "application/vnd.ms-excel");
  },


  // 下载 Word 方法
  word(data, fileName) {
    this.download0(data, fileName, "application/msword");
  },

  // 下载 Zip 方法
  zip(data, fileName) {
    this.download0(data, fileName, "application/zip");
  },

  // 下载 Html 方法
  html(data, fileName) {
    this.download0(data, fileName, "text/html");
  },

  // 下载 Markdown 方法
  markdown(data, fileName) {
    this.download0(data, fileName, "text/markdown");
  },
download0(data, fileName, mineType) {
    // 创建 blob
    let blob = new Blob([data], { type: mineType });
    // 创建 href 超链接,点击进行下载
    window.URL = window.URL || window.webkitURL;
    let href = URL.createObjectURL(blob);
    let downA = document.createElement("a");
    downA.href = href;
    downA.download = fileName;
    downA.click();
    // 销毁超连接
    window.URL.revokeObjectURL(href);
  },

不过它有个副作用。虽然这里有 Blob 的映射,但 Blob 本身只保存在内存中的。浏览器无法释放它。

在文档退出时(unload),该映射会被自动清除,因此 Blob 也相应被释放了。但是,如果应用程序寿命很长,那这个释放就不会很快发生。

因此,如果我们创建一个 URL,那么即使我们不再需要该 Blob 了,它也会被挂在内存中。

不过,URL.revokeObjectURL 可以从内部映射中移除引用,允许 Blob 被删除并释放内存。所以,在即时下载完资源后,不要忘记立即调用 URL.revokeObjectURL。

方法2. 转换为 base64(data:)

作为 URL.createObjectURL 的一个替代方法,我们也可以将 Blob 转换为 base64-编码的字符串。这种编码将二进制数据表示为一个由 0 到 64 的 ASCII 码组成的字符串,非常安全且“可读”。

更重要的是 —— 我们可以在 “data-url” 中使用此编码。“data-url” 的形式为 data:[<mediatype>][;base64],<data>。我们可以在任何地方使用这种 url,和使用“常规” url 一样。

FileReader 是一个对象,其唯一目的就是从 Blob 对象中读取数据,我们可以使用它的 readAsDataURL 方法将 Blob 读取为 base64。请看以下示例:

let blob = new Blob([res.data]); // res.data是后台返回的文件
 
let reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = (e) => {
    // 转换完成,创建一个a标签用于下载
    let a = document.createElement('a');
    a.download = fileName;
    a.href = e.target.result;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
}

在上述例子中,我们先实例化了一个 fileReader,用它来读取 blob。

一旦读取完成,就可以从 fileReader 的 result 属性中拿到一个data: URL 格式的 Base64 字符串。

最后,我们给 fileReader 注册了一个 onload 事件,在读取操作完成后开始下载。

两种方法总结与对比

URL.createObjectURL(blob) 可以直接访问,无需“编码/解码”,但需要记得撤销(revoke);

而 Data URL 无需撤销(revoke)任何操作,但对大的 Blob 进行编码时,性能和内存会有损耗。

总而言之,这两种从 Blob 创建 URL 的方法都可以用。但通常 URL.createObjectURL(blob) 更简单快捷。

responseType

export const fetchFile = async (params) => {
  return axios.get(api, {
    params,
    responseType: "blob"
  });
};

最后,我们回头说一下请求的注意点:如果你的项目使用的是 XHR (比如 axios)而不是 fetch, 那么请记得在请求时添加上 responseType 为 'blob'。

responseType 不是 axios 中的属性,而是 XMLHttpRequest 中的属性,它用于指定响应中包含的数据类型,当为 "blob" 时,表明 Response 是一个包含二进制数据的 Blob 对象。

除了 blob 之外,responseType 还有 arraybuffer、json、text等其他枚举字符串值。

扩展阅读

1. Blob

Blob 全称为 binary large object ,即二进制大对象,它是 JavaScript 中的一个对象,表示原始的类似文件的数据。下面是 MDN 中对 Blob 的解释:

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成  ReadableStream 来用于数据操作。

实际上,Blob 对象是包含有只读原始数据的类文件对象。简单来说,Blob 对象就是一个不可修改的二进制文件。

(1)Blob 创建

可以使用 Blob() 构造函数来创建一个 Blob:

new Blob(array, options);

其有两个参数:

  • array:由 ArrayBufferArrayBufferViewBlobDOMString 等对象构成的,将会被放进 Blob
  • options:可选的 BlobPropertyBag 字典,它可能会指定如下两个属性
    • type:默认值为 "",表示将会被放入到 blob 中的数组内容的 MIME 类型。
    • endings:默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入,不常用。

常见的 MIME 类型如下:

下面来看一个简单的例子:

const blob = new Blob(["Hello World"], {type: "text/plain"});

 这里可以成为动态文件创建,其正在创建一个类似文件的对象。这个 blob 对象上有两个属性:

  • size:Blob对象中所包含数据的大小(字节);
  • type:字符串,认为该Blob对象所包含的 MIME 类型。如果类型未知,则为空字符串。

下面来看打印结果:

const blob = new Blob(["Hello World"], {type: "text/plain"});

console.log(blob.size); // 11
console.log(blob.type); // "text/plain"

注意,字符串"Hello World"是 UTF-8 编码的,因此它的每个字符占用 1 个字节。

到现在,Blob 对象看起来似乎我们还是没有啥用。那该如何使用 Blob 对象呢?可以使用 URL.createObjectURL() 方法将将其转化为一个 URL,并在 Iframe 中加载:

<iframe></iframe>

const iframe = document.getElementsByTagName("iframe")[0];

const blob = new Blob(["Hello World"], {type: "text/plain"});

iframe.src = URL.createObjectURL(blob);

2. URL.createObjectURL()

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的 URL 对象表示指定的 File 对象或 Blob 对象。

备注: 此特性在 Web Worker 中可用

备注: 此特性在 Service Worker 中不可用,因为它有可能导致内存泄漏。

语法

objectURL = URL.createObjectURL(object);

参数 :object:用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象。

返回值 :一个DOMString包含了一个对象 URL,该 URL 可用于指定源 object的内容。

内存管理

在每次调用 createObjectURL() 方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL() 方法来释放。

浏览器在 document 卸载的时候,会自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。

3. URL.revokeObjectURL()

URL.revokeObjectURL() 静态方法用来释放一个之前已经存在的、通过调用 URL.createObjectURL() 创建的 URL 对象。当你结束使用某个 URL 对象之后,应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。

你可以在 sourceopen 被处理之后的任何时候调用 revokeObjectURL()。这是因为 createObjectURL() 仅仅意味着将一个媒体元素的 src 属性关联到一个 MediaSource 对象上去。调用revokeObjectURL() 使这个潜在的对象回到原来的地方,允许平台在合适的时机进行垃圾收集。

备注: 此特性在 Web Worker 中可用

语法:window.URL.revokeObjectURL(objectURL);

参数: 一个 objo'b'jDOMString,表示通过调用 URL.createObjectURL() 方法产生的 URL 对象。

返回值 undefined

4. FileReader.readAsDataURL()

readAsDataURL 方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成DONE,并触发 loadend 事件,同时 result 属性将包含一个data:URL 格式的字符串(base64 编码)以表示所读取文件的内容

语法 

readAsDataURL(blob)

 参数 blob即将被读取的 Blob 或 File 对象。

例子

<input type="file" onchange="previewFile()" /><br />
<img src="" height="200" alt="Image preview..." />

function previewFile() {
  var preview = document.querySelector("img");
  var file = document.querySelector("input[type=file]").files[0];
  var reader = new FileReader();

  reader.addEventListener(
    "load",
    function () {
      preview.src = reader.result;
    },
    false,
  );

  if (file) {
    reader.readAsDataURL(file);
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1195626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[C++随笔录] 红黑树

红黑树 红黑树的特点红黑树的模拟实现红黑树的底层结构insert的实现实现思路更新黑红比例的逻辑insert的完整代码 insert的验证 源码 红黑树的特点 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red或 Black。…

什么是Selenium?如何使用Selenium进行自动化测试?

什么是 Selenium&#xff1f; Selenium 是一种开源工具&#xff0c;用于在 Web 浏览器上执行自动化测试&#xff08;使用任何 Web 浏览器进行 Web 应用程序测试&#xff09;。   等等&#xff0c;先别激动&#xff0c;让我再次重申一下&#xff0c;Selenium 仅可以测试Web应用…

实现跨域必须要知道的知识点

目录 同源策略 cookie iframe和多窗口通信 片段识别符 window.postMessage() LocalStorage Storage接口&#xff1a; 概述 属性和方法 Storage.setItem() Storage.getItem() Storage.removeItem() Storage.clear() Storage.key() storage 事件 同源策略 浏览器…

响应式少儿舞蹈培训网站模板源码

模板信息&#xff1a; 模板编号&#xff1a;6903 模板编码&#xff1a;UTF8 模板颜色&#xff1a;橙色 模板分类&#xff1a;学校、教育、培训、科研 适合行业&#xff1a;培训机构类企业 模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#xf…

从开源项目聊鱼眼相机的“360全景拼接”

目录 概述 从360全景的背景讲起 跨过参数标定聊透视变化 拼接图片后处理 参考文献 概述 写这篇文章的原因完全源于开源项目(GitHub参阅参考文献1)。该项目涵盖了环视系统的较为全貌的制作过程&#xff0c;包含完整的标定、投影、拼接和实时运行流程。该篇文章主要是梳理全…

深入探析隔离CAN收发器NSI1050-DDBR各项参数

NSI1050-DDBR是一个隔离的CAN收发器&#xff0c;可以完全与ISO11898-2标准兼容。 NSI1050-DDBR集成了两个通道的数字隔离器和一个高电平可靠性CAN收发器。 数字隔离器是基于Novosense电容隔离技术的氧化硅隔离。 高度集成的解决方案可以帮助简化系统设计并提高可靠性。 NSI1050…

【postgresql】CentOS7 安装pgAdmin 4

CentOS7 安装PostgreSQL Web管理工具pgAdmin 4。 pgAdmin 是世界上最先进的开源数据库 PostgreSQL 最受欢迎且功能丰富的开源管理和开发平台。 下载地址&#xff1a; pgadmin-4 download pgAdmin 4分为桌面版和服务器版。 我们这里部署服务器版本。 安装RPM包。 安装源 s…

使用IDEA工具处理git合并后的冲突的细节

使用 IDEA 处理合并(merge) 使用IDEA处理git合并如果遇到冲突&#xff0c;对冲突文件的不冲突部分需要处理吗&#xff1f;会自动将双方不冲突的部分合并吗&#xff1f; 比如如下&#xff0c;使用 IDEA 合并 branch1 到 branch2 分支&#xff0c;出现了冲突&#xff0c;如下图…

Linux学习第二枪(yum,vim,g++/gcc,makefile的使用)

前言&#xff1a;在我的上一篇Linux博客我已经讲了基础指令和权限&#xff0c;现在我们来学习如何在Linux上运行和执行代码 一&#xff0c;yum yum是Linux中的软件包管理器&#xff0c;软件包是有人一些人写好的代码和程序作出软件包放到服务器上&#xff0c;我们使用yum就能…

DCMM咨询评估官方解答及各地补贴政策!

1、DCMM是什么&#xff1f; DCMM是国家标准GB/T 36073-2018《数据管理能力成熟度评估模型》&#xff08;Data management Capability Maturity Model&#xff09;的简称&#xff0c;是我国数据管理领域首个正式发布的国家标准&#xff0c;旨在帮助企业利用先进的数据管理理念和…

响应式青少年成长训练营培训网站模板源码

模板信息&#xff1a; 模板编号&#xff1a;28503 模板编码&#xff1a;UTF8 模板颜色&#xff1a;黑白 模板分类&#xff1a;学校、教育、培训、科研 适合行业&#xff1a;培训机构类企业 模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#x…

pyOCD

pyOCD 目录结构

Proteus仿真--基于数码管设计的可调式电子钟

本文主要介绍基于51单片机的数码管设计的可调式电子钟实验&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中数码管主要显示电子钟时间信息&#xff0c;按键用于调节时间 仿真运行视频 Proteus仿真--数码管设计的可调式电子钟&#xff08;仿真文件程…

C 语言 switch 语句

C 语言 switch 语句 在本教程中&#xff0c;您将通过一个示例学习在C语言编程中创建switch语句。 switch语句使我们可以执行许多代替方案中的一个代码块。 虽然您可以使用if…else…if阶梯执行相同的操作。但是&#xff0c;switch语句的语法更容易读写。 switch … case的语…

下载思科模拟器Cisco packet Tracer7.0

一、下载地址 主要去思科官网下载&#xff0c;但是最新版本没有汉化。 官网地址&#xff1a;https://www.cisco.com/ 百度网盘链接&#xff1a;https://pan.baidu.com/s/1VPTDN7BRgXIWt7m1E_8FMQ?pwd1111 提取码&#xff1a;1111 下载时把它放到U盘里&#xff0c;如图。 解压…

关于安卓刷机,小米手机,各种安卓手机,理念+实践,如:小米8

关于安卓刷机&#xff1a; 比刚买的时候 更加流畅&#xff0c;调节性能 狂暴模式&#xff0c;且无任何冗余软件和垃圾。 作为普通人&#xff0c;就是刷自己想要的系统&#xff0c;比如开发版。打开Root权限&#xff0c;第三方Rec TWRP&#xff0c;面具Magisk&#xff0c;LSPo…

超全总结!探索性数据分析 (EDA)方法汇总!

探索性数据分析&#xff08;EDA&#xff09;是一种系统地分析、可视化和总结数据集的过程&#xff0c;以获取洞察并更好地理解数据中潜在的模式和趋势。 EDA是任何数据分析项目中的重要步骤&#xff0c;因为它有助于识别数据中的潜在问题和偏见。EDA有助于为建模和进一步分析奠…

极智芯 | 存算一体 弯道超车的希望

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多经验分享 大家好&#xff0c;我是极智视界&#xff0c;本文分享一下 存算一体 弯道超车的希望。 邀您加入我的知识星球「极智视界」&#xff0c;星球内有超多好玩的项目实战源码和资源下载&#xff0c;链接&#xff1a;…

win11下安装odoo17(conda python11)

win11下安装odoo17 odoo17发行了&#xff0c;据说&#xff0c;UI做了很大改进&#xff0c;今天有空&#xff0c;体验一下 打开官方仓库&#xff1a; https://github.com/odoo/odoo 默认的版本已经变成17了 打开odoo/odoo/init.py&#xff0c;发现对python版本的要求也提高了…

揭秘南卡开放式耳机创新黑科技,核心技术剑指用户痛点

随着科技的进步和人们娱乐方式的升级&#xff0c;大家对听音工具的选择&#xff0c;从传统的耳机到蓝牙耳机再到AirPods这样的真无线耳机&#xff0c;而今年&#xff0c;也有一种全新的耳机爆发式涌入人们之中&#xff0c;那就是开放式耳机。 开放式耳机的出现&#xff0c;满足…