码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 前端实现图片下载的方法


    一、介绍

    ​ 在前一段时间的工作中,我在工作中遇到了一个点击按钮下载图片的场景,这在业务中是很常见的一种场景,但比较特别的是,我这次是需要在移动端的浏览器以及微信内部浏览器等场景下能实现点击下载。经过多次的探索和实践,我最终发现,以我目前的能力和能查阅的资料,在如今国内的复杂移动端浏览器环境下(百度浏览器、QQ浏览器、UC浏览器、夸克浏览器以及各大手机厂商的内置浏览器等等),他们各自拥有着不同的安全策略,在移动端实现一种覆盖所有主流浏览器的通用点击下载是不可能的,因为我所了解到的各种实现方法基本都是依赖于标签或者

    表单进行下载,但是在大多数移动端浏览器中,会默认对这种行为进行拦截,从而无法实现下载。

    ​ 而且还有一点是:在任何方案下,前端都无法绕过跨域的限制,所以需要图片所在的服务器对你当前域名开放权限,否则是无法下载的,最多能做到查看图片。

    ​ 最终解决方案为:跟需求方和产品沟通后,决定修改页面逻辑,提示用户长按保存图片,从而实现下载。当然如果哪位大佬还有跟好的方案能够实现在移动端的点击下载,那也欢迎在评论区多多指教分享。

    ​ 当然,我前期所做的各种方案的探索,自然也不是无用功,这些方案在PC端可以正常使用,只是在移动端会出现各种各样的问题,也正是因此有了这篇博客,分享多种图片下载的方法。

    二、具体方案

    1、直接使用标签的download属性(推荐)

    ​ 这是我最开始所尝试的方案,该方案的优点在于:代码简单清晰,只需在标签中增加download属性,并将图片地址赋给href属性即可。缺点在于:该方法只能下载同源URL或blob:、data: 协议的文件,对于非同源的图片只能实现查看。

    ​ 我的业务中想下载的图片,放置在另一台资源服务器下,不在同一个域名下,遂放弃该方案。

    实现代码:

    <a href="https://example.com/image.jpg" download="image.jpg">下载图片a>
    
    • 1
    2、canvas对象+标签(推荐)

    ​ 这是我之前PC端所实现的方案,通过canvas对象将图片转成base64格式,然后将其赋予标签的href属性,通过click事件触发下载。该方案可以在PC端实现下载同源图片或非同源但图片的服务器端设置了CORS的图片,但在移动端发现在小米(红米)、华为、vivo等手机的原生浏览器中可以下载,但是各大浏览器会拦截标签的下载行为,具体表现形式有所不同,但最终无法实现下载。

    实现代码:

    function download(item) {
      // 创建图片Image对象
      let image = new Image();
      // 以CORS的方式去请求这张图片 服务端需要设置CORS进行配合
      image.setAttribute("crossOrigin", "anonymous");
      // 图片加载完成后进行操作
      image.onload = function () {
        // 生成canvas图像
        let canvas = document.createElement("canvas");
        canvas.width = image.width;
        canvas.height = image.height;
        let context = canvas.getContext("2d");
        context.drawImage(image, 0, 0, image.width, image.height);
        // 得到图片的base64编码数据
        let url = canvas.toDataURL("image/png"); 
        // 生成一个a元素
        let a = document.createElement("a"); 
         // 创建一个单击事件
        let event = new MouseEvent("click");
        // 设置download属性 即下载后的文件名称
        a.download = item.diplomaCode || "photo"; 
        // 将生成的base64编码数据设置为a.href属性
        a.href = url; 
        // 触发a的单击事件 进行下载
        a.dispatchEvent(event); 
      };
      // 设置图片地址 开始加载
      image.src = item.imgUrl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    3、ajax请求(blob或base64格式)+标签(推荐)

    ​ 该方案是通过ajax请求,获取到blob或base64格式的图片文件流,然后在通过标签实现下载。该方法可以在PC端正常下载同源图片或非同源但图片的服务器端设置了CORS的图片,但在移动端同样会被各大浏览器拦截标签的下载行为,从而最终无法实现下载。

    ​ 如果想要获取blob类型的数据,则只需要前端配置ajax请求的responseType属性即可,无需后端配合。如果想要获取base64格式的数据,则需要后端进行配合修改数据,前端无需做特别配置。

    实现代码:

    // 获取blob类型的数据进行下载
    function download2(item) {
      // 创建ajax请求
      var xhr = new XMLHttpRequest();
      xhr.open("GET", item.imgUrl, true);
      // 设置请求的响应数据为 blob 类型
      xhr.responseType = "blob";
    	// 请求成功后
      xhr.onload = function () {
        if (this.status === 200) {
          // 将响应数据转换成blob对象
          var blob = new Blob([this.response], { type: "image/jpeg" });
          // 将blob转成一个url对象
          var url = URL.createObjectURL(blob);
          // 创建a标签并进行点击下载
          var a = document.createElement("a");
          a.href = url;
          a.download = item.diplomaCode || "photo";
          a.click();
          // 清除临时的url对象 解放内存
          URL.revokeObjectURL(url);
        }
      };
    	// 发送请求
      xhr.send();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    4、domtoimage+标签(不推荐)

    ​ 该方法是我在经过上面的实践之后发现,都是通过将数据转换成base64或blob类型的数据之后,然后再通过标签进行下载,然后我就想到了,将图片渲染到页面上之后,然后在通过domtoimage这个依赖包将图片的dom元素转成base64格式的图片,然后再下载。
    事实证明,这种方案多少有点脱裤子放屁的嫌疑,当然因为同上面一样的限制,无法在移动端渲染,以及无法实现对跨域图片的下载。

    实现代码:

    <img src="https://..." alt="" id="ZS1" />
    
    <script>
    function download3() {
      // 通过依赖根据图片渲染的dom元素生成base64格式的图片
      domtoimage.toPng(document.querySelector(`#ZS1`)).then((dataUrl) => {
        // 创建一个a元素
        let a = document.createElement("a"); 
        // 设置download属性
        a.download = "xkw证书"; 
        // 将生成的base64设置为a.href属性
        a.href = dataUrl; 
        // 点击下载
        a.click();
      });
    }
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    5、form表单(未验证,仅供参考)

    ​ 该方案是借助form表单元素实现图片的下载,需要服务端设置图片流的响应头Content-Disposition的值,相当于将图片以文件附件的形式进行下载,具体相关内容请查看参考文档中的第二、第三条。

    ​ 但该方案需要服务端进行配合,且会影响其他使用该图片的项目,因此我没有进行实际验证,如果有验证过的大佬,可以在评论区说一下。

    实现代码:

    function download4() {
    	// 创建一个隐藏的表单
    	const form = document.createElement('form');
    	form.style.display = 'none';
      // 请求地址即图片地址
    	form.action = '/imgurl....';
    	// 设置提交请求为 post
    	form.method = 'post';
    	form.target = '_blank';
    	document.body.appendChild(form);
    	const params = {;
    		name: 'test.jpg';
    	};
    	// 创建input来传递参数
    	for (let key in params) {;
    		let input = document.createElement('input');
    		input.type = 'hidden';
    		input.name = key;
    		input.value = params[key];
    		form.appendChild(input);
    	};
      // 进行提交请求
    	form.submit();
    	form.remove();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    6、iframe(未验证,不推荐)

    ​ 该方案只在IE中被支持,需要借助document.execCommand('SaveAs')命令,将图片通过iframe打开并保存,要求图片是同源的。

    实现代码:

    function download5(item) {
      const iframe = document.createElement("iframe");
      iframe.style.display = "none";
      iframe.onload = () => {
        iframe.contentWindow.document.execCommand("SaveAs");
        document.body.removeChild(iframe);
      };
      iframe.src = item.imgUrl;
      document.body.appendChild(iframe);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、参考文档

    MDN的a标签文档

    参考博客

    Content-Disposition

  • 相关阅读:
    队列(C语言实现)
    lnmp架构之mysql的主从复制(一)
    SQL SERVER 如何实现UNDO REDO 和PostgreSQL 有近亲关系吗
    牛客网verilog刷题知识点盘点(75道题的版本)
    开发中如何克服tomcat热部署弱的缺陷?看这篇文章就够了
    在博客文章中使用mermaid 定义流程图,序列图,甘特图
    Spring boot 自动装配原理
    Trie树的实现(代码实现) [Java]
    js基础笔记学习319练习2
    RestCloud ETL实践之无标识位实现增量数据同步
  • 原文地址:https://blog.csdn.net/weixin_45092437/article/details/134274801
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号