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

目录

前言

标签 download

a 标签链接下载的实现

1. 整体流程

2. 实现步骤

3. 类图

4. 代码示例 

download 使用注意点

1. 同源 URL 的限制

2. 不能携带 Header

Blob 转换

方法1. 用作 URL(blob:)

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

两种方法总结与对比

responseType


前言

在中后台项目中,前端难免需要处理下载的逻辑,需要下载的内容包括但不限于图片、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。请看以下示例:

mport downloadByUrl from "@/utils/download";

const download = async () => {
  const blob = await fetchFile();

  // 声明一个 fileReader
  const fileReader = new FileReader();
  
  // 将 blob 读取成 base64
  fileReader.readAsDataURL(blob);
  
  // 读取成功后 下载资源
  fileReader.onload = function () {
      downloadByUrl(fileReader.result);
  };
};

在上述例子中,我们先实例化了一个 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 卸载的时候,会自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

无人机航迹规划:五种最新智能优化算法(COA、SWO、KOA、GRO、LO)求解无人机路径规划MATLAB

一、五种算法&#xff08;LSO、SWO、KOA、GRO、LO&#xff09;简介 1、小龙虾优化算法COA 小龙虾优化算法&#xff08;Crayfsh optimization algorithm&#xff0c;COA&#xff09;由Jia Heming 等人于2023年提出&#xff0c;该算法模拟小龙虾的避暑、竞争和觅食行为&#xf…

数据分析实战 | 线性回归——女性身高与体重数据分析

目录 一、数据集及分析对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型训练 八、模型评价 九、模型调参 十、模型预测 实现回归分析类算法的Python第三方工具包比较常用的有statsmodels、statistics、scikit-learn等&#…

【CASS精品教程】cass 3d基于osgb三维模型生成等高线的两种方法

对于植被、房屋稀少的地方,可以基于osgb模型直接生成等高线。本文讲解在cass11.0 3d中基于osgb三维模型生成等高线的两种方法。 一、加载osgb三维模型 二、生成等高线 1. 绘制等高线 cass11版本提供了绘制单个等高线的功能。 点击【绘制等高线】,提示输入等高距。 输入固定…

MySQL的高阶语句

数据库的权限一般很小&#xff0c;工作中使用最多的场景就是查 排序、分组、子查询、视图、多表连接查询&#xff08;左连接、右连接、内连接&#xff09; create TABLE info ( id int(4) primary key, NAME varchar(5) not null, score decimal(5,2), address varchar(20)…

渗透测试必备工具--Metasploit(流程梳理与meterpreter权限分析)

目录 一、攻击前期准备必会的命令&#xff08;msf流程&#xff09; 1、启动&#xff1a;msfdb run 或者 msfconsole 2、Payload生成&#xff1a;msfvenom 3、查找相关模块&#xff1a;search 4、选择使用模块&#xff1a;use 5、返回上一层&#xff1a;back 6、查看需要…

猫罐头哪家好?宠物店自用的5款猫罐头推荐!猫咪嘎嘎炫~

亲爱的铲屎官们&#xff0c;你们是否会为猫咪选购猫罐头而感到烦恼&#xff1f;你们是否渴望了解哪些猫罐头在宠物界有着良好的口碑&#xff1f;猫罐头&#xff0c;作为猫咪日常饮食中的重要组成部分&#xff0c;其品质直接影响到猫咪的健康和幸福。 猫罐头哪家好&#xff1f;作…

go 引入包报错“构建约束排除‘D/...vendor/pkg包’”中所有的GO文件

解决方案&#xff1a; 方案一&#xff1a;没生效 go - 构建约束排除所有 Go 文件 - IT工具网 go modules - build constraints exclude all Go files in - Stack Overflow 方案二&#xff1a;生效&#xff0c;手动初始化创建一个目录 后续再研究原因&#xff0c;有明白的大…

边缘智能模型训练、推理的关键技术

目录 模型推理边缘智能模型推理的关键指标延迟&#xff08;Latency&#xff09;准确性&#xff08;Accuracy&#xff09;能效&#xff08;Energy&#xff09;隐私&#xff08;Privacy&#xff09;通讯/计算开销&#xff08;Comm/comp Overhead&#xff09; 关键技术模型压缩&am…

node插件MongoDB(三)—— 库mongoose 的使用和数据类型(一)

前言 提示&#xff1a;使用mongoose 的前提是你安装了node和 MongoDB。 mongoose 官网文档&#xff1a;http://mongoosejs.net/docs/index.html 文章目录 前言一、安装二、基本使用1. 打开bin目录的mongod.exe文件2. 基本使用的代码&#xff08;连接mongodb 服务&#xff09;3.…

LeetCode算法题解(回溯,难点)|LeetCode37. 解数独

LeetCode37. 解数独 题目链接&#xff1a;37. 解数独 题目描述&#xff1a; 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分…

【C++ 学习 ㉟】- 异常详解

目录 一、C 异常处理的基本语法 1.1 - 抛出异常 1.2 - 检测和捕获异常 二、在函数调用链中异常栈展开的匹配原则 三、异常重新抛出 四、异常规范 五、C 标准异常体系 程序的错误大致可以分为以下三种&#xff1a; 语法错误&#xff1a;在编译和链接阶段就能发现&#xf…

<蓝桥杯软件赛>零基础备赛20周--第5周--杂题-2

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

Java,多线程,线程的两种创建方式

首先是多线程的一些相关概念&#xff1a; 相关概念&#xff1a; 程序&#xff08;program&#xff09;&#xff1a;为完成特定任务&#xff0c;用某种语言编写的一组指令的集合。即指一段静态&#xff08;指不在执行中&#xff09;的代码。 进程&#xff08;process&#xf…

Qt QTableWidget表格的宽度

默认值 QTableWIdget的表格宽度默认是一个给定值&#xff0c;可以手动调整每列的宽度&#xff0c;也不填满父窗口 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {this->resize(800,600);QStringList contents{"11","111111111111",&…

聊聊模板引擎<Template engine>

模板引擎是什么 模板引擎是一种用于生成动态内容的工具&#xff0c;通常用于Web开发中。它能够将静态的模板文件和动态数据结合起来&#xff0c;生成最终的HTML、XML或其他文档类型。模板引擎通过向模板文件中插入变量、条件语句、循环结构等控制语句&#xff0c;从而实现根据…

操作系统:输入输出管理(一)系统概述与设备独立性软件

一战成硕 5.1 I/O系统概述5.1.1 I/O设备5.1.2 I/O控制方式5.1.3 I/O软件层次结构5.1.4 应用程序的I/O接口 5.2 设备独立性软件5.2.1 与设备无关的软件5.2.2 高速缓存与缓冲区5.2.3 设备分配与回收5.2.4 spooling技术&#xff08;假脱机技术&#xff09; 5.1 I/O系统概述 5.1.1…

一分钟秒懂人工智能对齐

文章目录 1.什么是人工智能对齐2.为什么要研究人工智能对齐3.人工智能对齐的常见方法 1.什么是人工智能对齐 人工智能对齐&#xff08;AI Alignment&#xff09;指让人工智能的行为符合人的意图和价值观。 人工智能系统可能会出现“不对齐”&#xff08;misalign&#xff09;…

【EI会议征稿】JPCS独立出版-第五届新材料与清洁能源国际学术会议(ICAMCE 2024)

JPCS独立出版-第五届新材料与清洁能源国际学术会议&#xff08;ICAMCE 2024&#xff09; 2024 5th International Conference on Advanced Material and Clean Energy 第五届新材料与清洁能源国际学术会议&#xff08;ICAMCE 2024&#xff09;将于2024年2月23-25日在中国▪长沙…

电机应用-无刷直流电机

无刷直流电机 无刷直流电机&#xff08;Brushless Dirent Current Motor&#xff0c;简称BLDCM&#xff09;由电动机主体和驱动器组成&#xff0c;无电刷和无换向器&#xff0c;是除了有刷电机外用得最多的一种电机。 无刷直流电机不使用机械的电刷装置&#xff0c;采用方波自控…

带你一分钟看懂 “kubernetes”

目录 什么是 Kubernetes Kubernetes 概述 为什么需要 Kubernetes&#xff0c;它能做什么&#xff1f; 什么是 Kubernetes 从官方网站上可以看到&#xff0c;它是一个工业级的容器编排平台。Kubernetes 这个单词是希腊语&#xff0c;它的中文翻译是“舵手”或者“飞行员”。在…