当前位置: 首页 > news >正文

前端实现生成图片并批量下载,下载成果物是zip包

简介

项目上有个需求,需要根据表单填写一些信息,来生成定制的二维码图片,并且支持批量下载二维码图片。
之前的实现方式是直接后端生成二维码图片,点击下载时后端直接返回一个zip包即可。但是项目经理说后端实现方式每次改个东西都要改大半天,所以让前端来实现。

方案

1.后端返回二维码的base64url数据流,就是下图红框中的二维码图片。

在这里插入图片描述
2.前端负责展示成交互上的二维码图片样式,如下图。

在这里插入图片描述
3.点击批量下载时,用户自己选择下载数量,然后后端返回二维码base64url的数组,前端自己实现下载,且是以zip的形式下载。下载的每张图片都是前端页面上所展示的样子。

思路

比如批量下载50个,首先10个一组处理,每个二维码编号生成一个blob流,塞入生成的zip中,50个二维码编号全部处理完成后,开始下载zip,将zip转为blob流,触发下载等待下载完成。

耗时较久的是每个二维码编号生成一个blob流,需要前端拿到后端返回的二维码base64 url, 通过js代码组装成最终的图片样式的DOM,然后需要塞到页面中,再使用dom-to-image 转成图片形式的blob流。

实现

1.比如选择下载数量是50张,点击下载,触发handleDownload 函数
2.使用jszip 生成一个zip
3.print_set_root 是该页面组件中最外层的div元素
4.分10个一组进行处理

完整代码如下:

import JSZip from 'jszip'
import { chunk } from 'lodash'
import domtoimage from 'dom-to-image'async handleDownload (val) {this.downBtnLoading = truetry {const { data } = await downQuas({ count: Number(val), randomNum: 6, start: this.ruleForm.code })this.zip = new JSZip()const rod = document.getElementById('print_set_root')const arr = chunk(data, 10)for (let i = 0; i < arr.length; i++) {await this.usePromiseArr(arr[i], rod)}this.downBtnLoading = falseconst that = thisthis.zip.generateAsync({ type: 'blob' }).then(function (base64) {const url = URL.createObjectURL(base64)const link = document.createElement('a')link.download = `${that.regionName}.zip`link.href = urllink.click()setTimeout(() => { window.URL.revokeObjectURL(url) })})} catch (e) {this.downBtnLoading = false}
},
usePromiseArr (data, rod) {const allPromise = []data.forEach(v => {allPromise.push(this.renderImg(v, rod))})return Promise.all(allPromise)
},
renderImg (data, rod) {return new Promise((resolve, reject) => {let num = 0const useSrc = `data:image/png;base64,${data.value}`const template2 = `<div class="title-normal">报修电话</div><div class="title">${this.ruleForm.phoneNumber || 'xxxxxxxx'}</div>`const leftDiv = document.createElement('div')leftDiv.setAttribute('class', 'left downLeft')leftDiv.setAttribute('id', 'erweima-common')const header = document.createElement('div')header.setAttribute('class', 'left-header')const large1 = document.createElement('div')large1.setAttribute('class', 'font-large')large1.textContent = 'xxxx'const large2 = document.createElement('div')large2.setAttribute('class', 'font-large')large2.textContent = 'xxxx'const topImage = document.createElement('div')topImage.setAttribute('class', 'top-img')const img1 = document.createElement('img')img1.src = '/xxxxxx.png'img1.onload = () => {topImage.appendChild(img1)num++this.downloadImg(num, leftDiv, rod, data.key, resolve)}header.appendChild(large1)header.appendChild(topImage)header.appendChild(large2)leftDiv.appendChild(header)const safe = document.createElement('div')safe.setAttribute('class', 'safe')safe.textContent = 'xxxxxxxxxx'const borderDiv = document.createElement('div')borderDiv.setAttribute('class', 'left-border')const dashedDiv = document.createElement('div')dashedDiv.setAttribute('class', 'dashed-border')const Img2 = document.createElement('img')Img2.src = '/xxxxxxxx.png'Img2.onload = () => {dashedDiv.appendChild(Img2)num++this.downloadImg(num, leftDiv, rod, data.key, resolve)}const title1 = document.createElement('div')title1.setAttribute('class', 'title')title1.textContent = 'xxxx'const title2 = document.createElement('div')title2.setAttribute('class', 'title')title2.textContent = 'xxxxxxxxxx'const title3 = document.createElement('div')title3.setAttribute('class', 'title-min')title3.textContent = 'Area Under 24-hour Monitoring'borderDiv.appendChild(dashedDiv)borderDiv.appendChild(title1)borderDiv.appendChild(title2)borderDiv.appendChild(title3)const border2 = document.createElement('div')border2.setAttribute('class', 'left-border')border2.innerHTML = template2const small = document.createElement('div')small.setAttribute('class', 'title-small')small.textContent = `${this.ruleForm.producer || 'xxxxxxxxx'}`const leftcontent = document.createElement('div')leftcontent.setAttribute('class', 'left-content')const useImg = document.createElement('img')useImg.setAttribute('class', 'erwei')useImg.src = useSrcuseImg.onload = () => {leftcontent.appendChild(useImg)leftcontent.appendChild(safe)leftcontent.appendChild(borderDiv)leftcontent.appendChild(border2)leftcontent.appendChild(small)leftDiv.appendChild(leftcontent)rod.appendChild(leftDiv)num++this.downloadImg(num, leftDiv, rod, data.key, resolve)}})
},
downloadImg (num, leftDiv, rod, name, resolve) {if (num !== 3) returnconst that = thisdomtoimage.toBlob(leftDiv).then(function (dataUrl) {rod.removeChild(leftDiv)that.zip.file(`${name}.jpeg`, dataUrl)resolve()})
}

使用技术:dom-to-image JSZip

注意点:元素在appendChild图片时,一定要等到图片onload后再执行appendChild操作。

相关文章:

前端实现生成图片并批量下载,下载成果物是zip包

简介 项目上有个需求&#xff0c;需要根据表单填写一些信息&#xff0c;来生成定制的二维码图片&#xff0c;并且支持批量下载二维码图片。 之前的实现方式是直接后端生成二维码图片&#xff0c;点击下载时后端直接返回一个zip包即可。但是项目经理说后端实现方式每次改个东西…...

android 快速实现 圆角矩形控件 及 圆形控件

1.自定义RoundImageView package com.examle.widget;import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import an…...

【Python】外网远程登录访问jupyter notebook+pycharm使用ipython

第一步&#xff1a;创建python虚拟环境 conda create -n py3610 python3.6.10第二步&#xff1a;安装ipython pip install ipython pip install ipython notebook第三步&#xff1a;创建 IPython Notebook 服务器配置文件 # 进入python交互shell&#xff0c;设置密码 >&…...

error:0308010C:digital envelope routines::unsupported

error:0308010C:digital envelope routines::unsupported 报错原因解决方案方案一&#xff1a;降低node版本在17以下指定node版本 mac node版本降级 mac切换node版本 方案二&#xff1a;启用legacy OpenSSL provider方案三&#xff1a;配置package.json文件拓展&#xff1a;pac…...

Vue前端的工作需求

加油&#xff0c;新时代打工人&#xff01; 需求&#xff1a; 实现带树形结构的表格&#xff0c;父数据显示新增下级&#xff0c;和父子都显示编辑。 技术&#xff1a; Vue3 Element Plus <template><div><el-table:data"tableData"style"width…...

97. 常用的HTTP服务压测工具

文章目录 导言一、ab二、wrk三、go-wrk 导言 在项目正式上线之前&#xff0c;我们通常需要通过压测来评估当前系统能够支撑的请求量、排查可能存在的隐藏bug&#xff0c;同时了解了程序的实际处理能力能够帮我们更好的匹配项目的实际需求(服务器实例个数&#xff0c;如需要部署…...

活动预告|听云猿生数据创始人 CEO 曹伟分享云数据库行业十余年经验总结

3月16日&#xff0c;KubeBlocks 将携手 OceanBase 开源社区、AutoMQ 带来《LLMs 时代下的企业数据管理与降本增效之路》主题 meetup&#xff0c;扫描下方二维码&#xff0c;即刻报名&#x1f447;。 云猿生数据创始人 & CEO 曹伟将带来《KubeBlocks&#xff1a;把所有数据…...

数仓实战——京东数据指标体系的构建与实践

目录 一、如何理解指标体系 1.1 指标和指标体系的基本含义 1.2 指标和和标签的区别 1.3 指标体系在数据链路中的位置和作用 1.4 流量指标体系 1.5 指标体系如何向上支撑业务应用 1.6 指标体系背后的数据加工逻辑 二、如何搭建和应用指标体系 2.1 指标体系建设方法—OS…...

Alias许可配置

在数字化时代&#xff0c;软件已成为企业竞争的核心要素。然而&#xff0c;随着软件市场的日益复杂&#xff0c;如何合理配置和使用软件许可&#xff0c;已成为企业亟待解决的问题。Alias许可配置服务&#xff0c;凭借其卓越的功能和性能&#xff0c;帮助企业优化软件使用&…...

【读书笔记】针对ICS的ATTCK矩阵详解(一)

Techniques - ICS | MITRE ATT&CKhttps://attack.mitre.org/techniques/ics/ 一、初始访问&#xff08;Initial Access&#xff09; 该阶段&#xff1a;攻击者正在尝试进入ICS环境。 初始访问包括攻击者可能用作入口向量&#xff0c;从而可以在 ICS 环境中获得初始立足点的…...

Rust多线程访问数据,推荐使用mutex还是channel?

在Rust中&#xff0c;选择使用互斥锁&#xff08;mutex&#xff09;还是通道&#xff08;channel&#xff09;来进行多线程间的数据访问&#xff0c;主要取决于你的具体需求和数据共享的模式。 互斥锁&#xff08;Mutex&#xff09; 互斥锁是一种同步原语&#xff0c;用于保护…...

基于pytorch的手写体识别

一、环境搭建 链接: python与深度学习——基础环境搭建 二、数据集准备 本次实验用的是MINIST数据集&#xff0c;利用MINIST数据集进行卷积神经网络的学习&#xff0c;就类似于学习单片机的点灯实验&#xff0c;学习一门机器语言输出hello world。MINIST数据集&#xff0c;可以…...

Leetcode 56. 合并区间

题目描述&#xff1a;以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xf…...

C++:List的使用和模拟实现

创作不易&#xff0c;感谢三连&#xff01;&#xff01; 一、List的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不…...

20个Python函数程序实例

前面介绍的函数太简单了&#xff1a; 以下是 20 个不同的 Python 函数实例 下面深入一点点&#xff1a; 以下是20个稍微深入一点的&#xff0c;使用Python语言定义并调用函数的示例程序&#xff1a; 20个函数实例 简单函数调用 def greet():print("Hello!")greet…...

Wireshark——获取捕获流量的前N个数据包

1、问题 使用Wireshark捕获了大量的消息&#xff0c;但是只想要前面一部分。 2、方法 使用Wireshark捕获了近18w条消息&#xff0c;但只需要前5w条。 选择文件&#xff0c;导出特定分组。 输入需要保存的消息范围。如&#xff1a;1-50000。 保存即可。...

006-浏览器输入域名到返回

浏览器输入域名到返回 1、URL 输入2、DNS 域名解析3、建立 TCP 连接三次握手概念三次握手理解 4、发送 HTTP/HTTPS 请求5、服务器处理&#xff0c;并返回响应6、浏览器解析并渲染页面7、请求结束&#xff0c;端口 TCP 连接四次挥手概念四次挥手理解 1、URL 输入 2、DNS 域名解析…...

【kubernetes】关于k8s集群如何将pod调度到指定node节点?

目录 一、k8s的watch机制 二、scheduler的调度策略 Predicate&#xff08;预选策略&#xff09; 常见算法&#xff1a; priorities&#xff08;优选策略&#xff09;常见的算法有&#xff1a; 三、k8s的标签管理之增删改查 四、k8s的将pod调度到指定node的方法 方案一&am…...

【框架】React和Vue的异同

1. 前言 React对于原生JS要求会高一级&#xff0c;国外React用的多&#xff0c;国内Vue用的多。 2. 共同点 组件化函数式编程 &#xff08;vue3函数式编程、vue2声明式编程&#xff09;单向数据流&#xff0c;数据驱动视图VirtualDOM Diff算法操作DOM社区成熟&#xff0c;…...

如何选择阅读软件技术学习书籍

如何选择阅读软件技术学习书籍 这里以软件技术学习的角度结合自身感悟谈谈&#xff0c;如何选择阅读书籍。 人的时间和精力都是非常有限的&#xff0c;软件技术学习者如何选择阅读书籍。以下是从我的经验教训总结的一些体会&#xff1a; 1、确定自己的兴趣领域和阅读目标 选…...

腾讯开源翻译大模型HY-MT1.5-7B镜像使用教程:新手快速入门

腾讯开源翻译大模型HY-MT1.5-7B镜像使用教程&#xff1a;新手快速入门 你是否曾为寻找一个既强大又好用的翻译工具而烦恼&#xff1f;无论是阅读外文资料、处理多语言客服&#xff0c;还是开发一个需要实时翻译的应用&#xff0c;找到一个靠谱的翻译引擎总是关键一步。今天&am…...

Android项目中的Gradle文件详解:从基础配置到高级技巧

Android项目中的Gradle文件详解&#xff1a;从基础配置到高级技巧 在Android开发的世界里&#xff0c;Gradle文件就像是一个项目的"大脑"&#xff0c;它控制着构建过程的方方面面。对于有一定经验的Android开发者来说&#xff0c;深入理解Gradle文件的配置不仅能够提…...

3步解锁魔兽争霸III最佳体验:WarcraftHelper全方位优化工具指南

3步解锁魔兽争霸III最佳体验&#xff1a;WarcraftHelper全方位优化工具指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper WarcraftHelper是一款专为…...

Kook Zimage 真实幻想 Turbo在软件测试中的应用:自动化UI设计验证

Kook Zimage 真实幻想 Turbo在软件测试中的应用&#xff1a;自动化UI设计验证 1. 引言&#xff1a;UI设计验证的痛点与机遇 在软件开发流程中&#xff0c;UI设计验证一直是个让人头疼的环节。测试人员需要对照设计稿&#xff0c;逐个像素检查界面元素的位置、颜色、字体和布局…...

使用MATLAB进行DeOldify结果的后处理与定量分析

使用MATLAB进行DeOldify结果的后处理与定量分析 如果你是一位习惯在MATLAB环境中工作的研究人员或工程师&#xff0c;当你想对DeOldify这类AI图像上色工具的输出结果进行更深入的评估时&#xff0c;可能会觉得缺少趁手的分析工具。直接看效果图固然直观&#xff0c;但如何量化…...

Qwen3.5-2B入门指南:WebUI中Clear Image按钮对多轮图文对话的影响

Qwen3.5-2B入门指南&#xff1a;WebUI中Clear Image按钮对多轮图文对话的影响 1. 认识Qwen3.5-2B轻量化多模态模型 Qwen3.5-2B是Qwen3.5系列中的轻量级版本&#xff0c;仅有20亿参数规模。这个模型特别适合在资源有限的设备上运行&#xff0c;比如个人电脑、边缘计算设备等。…...

电路设计与漫画艺术的跨界融合

1. 当电路遇见漫画&#xff1a;工程师的艺术表达在大多数人眼中&#xff0c;电路设计是冰冷的数据和复杂的公式&#xff0c;而漫画则是天马行空的创意表达。但作为一名从业十年的硬件工程师&#xff0c;我发现这两者其实有着惊人的相似之处——它们都需要严谨的结构设计&#x…...

ostringstream清空缓存的正确姿势:str()与clear()的深度解析

1. 为什么ostringstream清空缓存这么让人困惑&#xff1f; 第一次用ostringstream的时候&#xff0c;我也被它坑过。记得当时写了个日志记录功能&#xff0c;反复往同一个ostringstream对象里写入内容&#xff0c;结果发现每次输出的日志都越积越长。我本能地调用了clear()&…...

AI Agent与传统RPA工具有什么本质区别?2026深度解析企业级智能体进化路径

在2026年3月下旬的当下&#xff0c;全球自动化技术正经历着从“按图索骥”到“自主导航”的范式跃迁。随着GPT-5.4等具备原生电脑操作能力的大模型发布&#xff0c;以及开源项目OpenClaw在过去一周内的爆发式增长&#xff0c;**AI Agent与传统RPA工具有什么本质区别&#xff1f…...

物联网水产养殖解决方案:全域监控,数据驱动科学养殖

一、方案前言水产养殖作为我国农业支柱产业之一&#xff0c;是保障民生水产品供应的核心板块&#xff0c;当前正面临从传统粗放式养殖向现代化、精准化、绿色化养殖转型的关键节点。随着养殖密度提升、环保要求趋严、市场对高品质水产品需求增长&#xff0c;以及劳动力成本攀升…...