前端实现下载的触发机制,一篇文章搞定下载(文件下载,图片下载,截屏下载)

本文主要是介绍前端实现下载的触发机制,一篇文章搞定下载(文件下载,图片下载,截屏下载),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

      • 一、下载方式
        • 1.浏览器下载
        • 2.a标签触发下载
          • 什么是base64
          • 自定义下载文件名
      • 二、实战下载
        • 1.文件下载
        • 2.图片下载
        • 3.截屏下载(解决HTML2cavas截屏空白问题)

一、下载方式

一般来说,前端下载,一般分为利用浏览器下载,和a标签触发下载

1.浏览器下载

有时候我们会发现一张图片的路径如果直接输入在浏览器地址栏中就会直接触发下载

例如下面图片

http://webond.tpddns.cn:8823/facility/tempImage.json?path=upload/196afac1-3e44-47df-910b-8783a9807f00.png

但有些图片放入浏览器的地址栏却并没有触发下载,而是在也浏览器中出现图片预览效果

例如:

https://img-blog.csdnimg.cn/2021052711301061.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hcmVuZHU=,size_16,color_FFFFFF,t_70

探求其原因,我们分别查看了2者了请求头

发现能触发下载的图片请求头存在该属性Content-Disposition: attachment

在这里插入图片描述

后端实现下载

浏览器下载,本质其实是后端在返回的响应头中添加

Content-Disposition:attachment; filename=’下载的文件名‘

那么只要是在浏览器地址栏中访问就能直接触发下载,

form表单下载

我们常用form表单,将下载的url在一个iframe中打开,来触发下载

2.a标签触发下载

对于上述这种存在下载头的url,使用a标签也能触发下载,(同样也是通过浏览器下载,如果没有下载头,a标签会预览该图片)

<a herf='url' download='下载的文件名' /> 

a标签触发下载,一种是上面的带下载头的,还有2种 dataUrl(base64), 和 blobUrl(流),这2种也能直接触发下载,也是用来实现纯前端下载的方式,如前端构建excel,截屏等,然后转换成blobUrl实现下载,对于base64想必大多数人都有疑惑,

什么是base64

基于64个可打印字符来表示二进制数据,在HTTP环境下传递较长的标识信息

简单说就是一串有内容的编码,例如在html中,使用base64的图片,将不会加载请求,因为这串编码就是这张图片,弊端就是浏览器也不会缓存这张图片,因为不是远程加载 的图片

如下:上传文件并转换为base64

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><input type="file" id='fileId'>
</body>
<script>// 上传图片转换成base64let upDOM = document.getElementById('fileId')upDOM.addEventListener('change', (e) => {console.log(e)console.log(upDOM.files[0])var reader = new FileReader();reader.readAsDataURL(upDOM.files[0]);reader.onload = function (ie) {var Base64Val = this.result;console.log('Base64')console.log(Base64Val)}})
</script>
</html>

通过上述例子,大家可以发现base64很长,所以当图片过大时,使用base64来下载,就会下载失败,因为超出了get的长度限制,所以,大部分情况下,不会直接使用base64,而是进一步转换为blobUrl文件流

// base64转换为blob流function  convertBase64UrlToBlob(base64) {var parts = base64.dataURL.split(";base64,");var contentType = parts[0].split(":")[1];var raw = window.atob(parts[1]);var rawLength = raw.length;var uInt8Array = new Uint8Array(rawLength);for (var i = 0; i < rawLength; i++) {uInt8Array[i] = raw.charCodeAt(i);}return new Blob([uInt8Array], {type: contentType});}
var blob = convertBase64UrlToBlob (base64)var blobUrl= URL.createObjectURL(blob);
自定义下载文件名

​ 一般情况下,下载的文件名字为后端定义好的文件名,或者是服务器存储的文件名,前端如果要修改,可以利用a标签的download 属性,自己定义下载的文件名,

<a herf='url' download='下载的文件名' />

但要注意一点,能使用download修改名字,要么是同源,要么只能是dataUrl 和bolbUrl,否则无法修改下载的名字

二、实战下载

1.文件下载

一般类似exsel这类下载文件,是需要给后台提交数据来下载的,一般也为post接口,对于这样的下载,

第一种方式使用表单提交 后台也需要接受的是formdata数据

如果页面有现有的表单

form表单下载

没有就用js创建一个隐形的form来提交表单

第二种方式还是使用ajax 后台接收正常的json即可

使用ajax是无法直接下载的,请求成功后,后台会返回一个文件流

看见会是这样的乱码但没什么影响的
在这里插入图片描述

request 为引入的封装好的axios

下面有几个点需要注意

1.是要定义响应的type ,responseType: “arraybuffer”,告诉服务器需要返回一个buffer,不然下载下来很可能 是乱码
在这里插入图片描述

2.我们需要在返回的header把名字取下来,

const fileName = res.headers['content-disposition'].split('=')[1]
//有时候,后端定义的fileName,无效,下载下来还是服务器存储的名字

​ 但是会发现明明header里面有就是取不到Content-Disposcition,

这个是响应头Access-Control-Expose-Headers规定的可以暴露给外部的header

MDN

默认情况下,只有七种 simple response headers (简单响应首部)可以暴露给外部:

  • Cache-Control
  • Content-Language
  • Content-Length
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

如果想要让客户端可以访问到其他的首部信息,可以将它们在 Access-Control-Expose-Headers 里面列出来。

所以后端只需要配置

response.setHeader("Access-Control-Expose-Headers","Content-Disposcition")

在这里插入图片描述

  function exportFile(url,data){request({//告诉服务器需要返回一个buffer,不然下载下来很可能		是乱码responseType: "arraybuffer",url: url,method: 'post',data: data}).then(res=>{const content = res.data//转换成blob对象const blob = new Blob([content],{ type: 'application/vnd.ms-excel;charset=utf-8' })//获取文件名字const fileName = res.headers['content-disposition'].split('=')[1]const elink = document.createElement('a')elink.download = decodeURI(fileName)elink.style.display = 'none'console.log(res);//生成blobUrlelink.href = URL.createObjectURL(blob)document.body.appendChild(elink)elink.click()URL.revokeObjectURL(elink.href) // 释放URL 对象document.body.removeChild(elink)})}
2.图片下载

如果是图片流,下载方式同上

但大多数时候,是使用一个url来下载

a href="http://webond.tpddns.cn:8823/facility/tempImage.json?path=upload/196afac1-3e44-47df-910b-8783a9807f00.png" download="修改的名字.png">点击</a>

上面提到过 需要后端设置下载头才能触发,但是因为不是同源无法修改名字

image-20210527152922083

但如果想自定义名字,同时也不需要后端设置下载头呢

这个时候我们就需要利用cavas转换一下,

tips注意 cavas,必须要跨越的图片,否则画出来就是空白,同时后台需要设置跨越 指定域名或者*,不然会报跨越的错误,同时图片也无法加载

image-20210527153736217

img.crossOrigin = "Anonymous";  //设置图片跨越,没有跨越cavas会被污染无法画出
     // 转换为base64function    convertUrlToBase64(url) {return new Promise(function (resolve, reject) {var img = new Image();//设置图片跨越,没有跨越cavas会被污染无法画出img.crossOrigin = "Anonymous";img.src = url;img.onload = function () {var canvas = document.createElement("canvas");canvas.width = img.width;canvas.height = img.height;var ctx = canvas.getContext("2d");ctx.drawImage(img, 0, 0, img.width, img.height);var ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();//转换为base64var dataURL = canvas.toDataURL("image/" + ext);var base64 = {dataURL: dataURL,type: "image/" + ext,ext: ext};resolve(base64);};});}
   // base64转换为blob流function    convertBase64UrlToBlob(base64) {var parts = base64.dataURL.split(";base64,");var contentType = parts[0].split(":")[1];var raw = window.atob(parts[1]);var rawLength = raw.length;var uInt8Array = new Uint8Array(rawLength);for (var i = 0; i < rawLength; i++) {uInt8Array[i] = raw.charCodeAt(i);}return new Blob([uInt8Array], {type: contentType});}
  // 判断浏览器function  myBrowser() {var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串if (userAgent.indexOf("OPR") > -1) {return "Opera";} //判断是否Opera浏览器 OPR/43.0.2442.991if (userAgent.indexOf("Firefox") > -1) {return "FF";} //判断是否Firefox浏览器  Firefox/51.0if (userAgent.indexOf("Trident") > -1) {return "IE";} //判断是否IE浏览器 Trident/7.0; rv:11.0if (userAgent.indexOf("Edge") > -1) {return "Edge";} //判断是否Edge浏览器 Edge/14.14393if (userAgent.indexOf("Chrome") > -1) {return "Chrome";} // Chrome/56.0.2924.87if (userAgent.indexOf("Safari") > -1) {return "Safari";} //判断是否Safari浏览器 AppleWebKit/534.57.2 Version/5.1.7 Safari/534.57.2}

下载图片

 var url = 'https://img-blog.csdnimg.cn/2021052711301061.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hcmVuZHU=,size_16,color_FFFFFF,t_70'var that = thisvar fileName = '自定义的名字'convertUrlToBase64(url).then(function (base64) {console.log(base64);// 图片转为base64returnvar blob = that.convertBase64UrlToBlob(base64); // 转为blob对象// 下载if (that.myBrowser() == "IE") {window.navigator.msSaveBlob(blob, fileName + ".png");} else if (that.myBrowser() == "FF") {window.location.href = url;} else {var a = document.createElement("a");a.download = fileName;a.href = URL.createObjectURL(blob);document.body.appendChild(a)a.click();URL.revokeObjectURL(a.href) // 释放URL 对象document.body.removeChild(a)}});

将上面几个代码和在一起就是完整的转换下载

3.截屏下载(解决HTML2cavas截屏空白问题)

有些时候,我们需要对图片进行处理拼接等,这个时候需要用到截屏,我们可以使用插件html2cavas,来做到截屏效果

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.js"></script><style>#content {background: red;display: flex;justify-content: center;align-items: center;width: 500px;height: 500px;}#capture{margin-left: 20px;width: 500px;height: 500px;}div{float: left;}img {width: 90%;height: auto;}</style>
</head><body><div id="content"><img src="https://img-blog.csdnimg.cn/2021052711301061.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hcmVuZHU=,size_16,color_FFFFFF,t_70"alt=""></div><div id="capture"><p>截屏的图</p></div><button onclick="capture()">截屏</button>
</body></html>
<script>function capture(data) {const el = document.getElementById('content')html2canvas(el).then(canvas => {const imgUrl = canvas.toDataURL("image/png"); // 获取生成的图片的url console.log('imgUrl');console.log(imgUrl);var img = new Image();img.src = imgUrl;document.getElementById('capture').appendChild(img)})}
</script>

在这里插入图片描述

但是发现并没有把图片截取下来,原因就是上面说的图片没跨域无法在cavas上展现

我们需要3点配置,

1.图片上设置跨域crossorigin=“anonymous”

<img crossorigin="anonymous" src=''/>

2.html2cavas 跨域useCORS:true

html2canvas(el,{useCORS:true})

3.后台配置跨域头
在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.js"></script><style>#content {background: red;display: flex;justify-content: center;align-items: center;width: 500px;height: 500px;}#capture {margin-left: 20px;width: 500px;height: 500px;}div {float: left;}img {width: 90%;height: auto;}</style>
</head><body><div id="content"><!--  crossorigin="anonymous" 配置图片跨域 --><img crossorigin="anonymous" src="https://img-blog.csdnimg.cn/2021052711301061.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hcmVuZHU=,size_16,color_FFFFFF,t_70"alt=""></div><div id="capture"><p>截屏的图</p></div><button onclick="capture()">截屏</button>
</body></html>
<script>function capture(data) {const el = document.getElementById('content')// useCORS 配置html2canvas跨越html2canvas(el,{useCORS:true}).then(canvas => {const imgUrl = canvas.toDataURL("image/png"); // 获取生成的图片的url console.log('imgUrl');console.log(imgUrl);var img = new Image();img.src = imgUrl;document.getElementById('capture').appendChild(img)})}
</script>

在这里插入图片描述

如果觉得默认截图过小,或者需要其他配置参考文档配置

html2cavas

下面就是完整截屏下载代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.js"></script><style>#content {background: red;display: flex;justify-content: center;align-items: center;width: 500px;height: 500px;}#capture {margin-left: 20px;width: 500px;height: 500px;}div {float: left;}img {width: 90%;height: auto;}</style>
</head><body><div id="content"><!--  crossorigin="anonymous" 配置图片跨域 --><img crossorigin="anonymous"src="https://img-blog.csdnimg.cn/2021052711301061.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21hcmVuZHU=,size_16,color_FFFFFF,t_70"alt=""></div><div id="capture"><p>截屏的图</p></div><button onclick="capture()">截屏</button>
</body></html>
<script>var that = thisfunction capture(data) {const el = document.getElementById('content')// useCORS 配置html2canvas跨越html2canvas(el, {useCORS: true}).then(canvas => {const imgUrl = canvas.toDataURL("image/png"); // 获取生成的图片的url console.log('imgUrl');console.log(imgUrl);var img = new Image();img.src = imgUrl;document.getElementById('capture').appendChild(img)var blob = this.convertBase64UrlToBlob(imgUrl)const name = '捕获的图片.png'var a = document.createElement("a");a.download = decodeURI(name);a.href = URL.createObjectURL(blob);document.body.appendChild(a)a.click();URL.revokeObjectURL(a.href) // 释放URL 对象document.body.removeChild(a)})}// 转换成blob对象function convertBase64UrlToBlob(base64) {var parts = base64.split(";base64,");var contentType = parts[0].split(":")[1];var raw = window.atob(parts[1]);var rawLength = raw.length;var uInt8Array = new Uint8Array(rawLength);for (var i = 0; i < rawLength; i++) {uInt8Array[i] = raw.charCodeAt(i);}return new Blob([uInt8Array], {type: contentType});}
</script>

这篇关于前端实现下载的触发机制,一篇文章搞定下载(文件下载,图片下载,截屏下载)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/641946

相关文章

Spring Boot整合Redis注解实现增删改查功能(Redis注解使用)

《SpringBoot整合Redis注解实现增删改查功能(Redis注解使用)》文章介绍了如何使用SpringBoot整合Redis注解实现增删改查功能,包括配置、实体类、Repository、Se... 目录配置Redis连接定义实体类创建Repository接口增删改查操作示例插入数据查询数据删除数据更

Java Lettuce 客户端入门到生产的实现步骤

《JavaLettuce客户端入门到生产的实现步骤》本文主要介绍了JavaLettuce客户端入门到生产的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录1 安装依赖MavenGradle2 最小化连接示例3 核心特性速览4 生产环境配置建议5 常见问题

linux ssh如何实现增加访问端口

《linuxssh如何实现增加访问端口》Linux中SSH默认使用22端口,为了增强安全性或满足特定需求,可以通过修改SSH配置来增加或更改SSH访问端口,具体步骤包括修改SSH配置文件、增加或修改... 目录1. 修改 SSH 配置文件2. 增加或修改端口3. 保存并退出编辑器4. 更新防火墙规则使用uf

Java 的ArrayList集合底层实现与最佳实践

《Java的ArrayList集合底层实现与最佳实践》本文主要介绍了Java的ArrayList集合类的核心概念、底层实现、关键成员变量、初始化机制、容量演变、扩容机制、性能分析、核心方法源码解析、... 目录1. 核心概念与底层实现1.1 ArrayList 的本质1.1.1 底层数据结构JDK 1.7

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关