前端实现生成图片并批量下载,下载成果物是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包
简介 项目上有个需求,需要根据表单填写一些信息,来生成定制的二维码图片,并且支持批量下载二维码图片。 之前的实现方式是直接后端生成二维码图片,点击下载时后端直接返回一个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
第一步:创建python虚拟环境 conda create -n py3610 python3.6.10第二步:安装ipython pip install ipython pip install ipython notebook第三步:创建 IPython Notebook 服务器配置文件 # 进入python交互shell,设置密码 >&…...
error:0308010C:digital envelope routines::unsupported
error:0308010C:digital envelope routines::unsupported 报错原因解决方案方案一:降低node版本在17以下指定node版本 mac node版本降级 mac切换node版本 方案二:启用legacy OpenSSL provider方案三:配置package.json文件拓展:pac…...
Vue前端的工作需求
加油,新时代打工人! 需求: 实现带树形结构的表格,父数据显示新增下级,和父子都显示编辑。 技术: Vue3 Element Plus <template><div><el-table:data"tableData"style"width…...
97. 常用的HTTP服务压测工具
文章目录 导言一、ab二、wrk三、go-wrk 导言 在项目正式上线之前,我们通常需要通过压测来评估当前系统能够支撑的请求量、排查可能存在的隐藏bug,同时了解了程序的实际处理能力能够帮我们更好的匹配项目的实际需求(服务器实例个数,如需要部署…...
活动预告|听云猿生数据创始人 CEO 曹伟分享云数据库行业十余年经验总结
3月16日,KubeBlocks 将携手 OceanBase 开源社区、AutoMQ 带来《LLMs 时代下的企业数据管理与降本增效之路》主题 meetup,扫描下方二维码,即刻报名👇。 云猿生数据创始人 & CEO 曹伟将带来《KubeBlocks:把所有数据…...
数仓实战——京东数据指标体系的构建与实践
目录 一、如何理解指标体系 1.1 指标和指标体系的基本含义 1.2 指标和和标签的区别 1.3 指标体系在数据链路中的位置和作用 1.4 流量指标体系 1.5 指标体系如何向上支撑业务应用 1.6 指标体系背后的数据加工逻辑 二、如何搭建和应用指标体系 2.1 指标体系建设方法—OS…...
Alias许可配置
在数字化时代,软件已成为企业竞争的核心要素。然而,随着软件市场的日益复杂,如何合理配置和使用软件许可,已成为企业亟待解决的问题。Alias许可配置服务,凭借其卓越的功能和性能,帮助企业优化软件使用&…...
【读书笔记】针对ICS的ATTCK矩阵详解(一)
Techniques - ICS | MITRE ATT&CKhttps://attack.mitre.org/techniques/ics/ 一、初始访问(Initial Access) 该阶段:攻击者正在尝试进入ICS环境。 初始访问包括攻击者可能用作入口向量,从而可以在 ICS 环境中获得初始立足点的…...
Rust多线程访问数据,推荐使用mutex还是channel?
在Rust中,选择使用互斥锁(mutex)还是通道(channel)来进行多线程间的数据访问,主要取决于你的具体需求和数据共享的模式。 互斥锁(Mutex) 互斥锁是一种同步原语,用于保护…...
基于pytorch的手写体识别
一、环境搭建 链接: python与深度学习——基础环境搭建 二、数据集准备 本次实验用的是MINIST数据集,利用MINIST数据集进行卷积神经网络的学习,就类似于学习单片机的点灯实验,学习一门机器语言输出hello world。MINIST数据集,可以…...
Leetcode 56. 合并区间
题目描述:以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入…...
C++:List的使用和模拟实现
创作不易,感谢三连!! 一、List的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 2. list的底层是双向链表结构,双向链表中每个元素存储在互不…...
20个Python函数程序实例
前面介绍的函数太简单了: 以下是 20 个不同的 Python 函数实例 下面深入一点点: 以下是20个稍微深入一点的,使用Python语言定义并调用函数的示例程序: 20个函数实例 简单函数调用 def greet():print("Hello!")greet…...
Wireshark——获取捕获流量的前N个数据包
1、问题 使用Wireshark捕获了大量的消息,但是只想要前面一部分。 2、方法 使用Wireshark捕获了近18w条消息,但只需要前5w条。 选择文件,导出特定分组。 输入需要保存的消息范围。如:1-50000。 保存即可。...
006-浏览器输入域名到返回
浏览器输入域名到返回 1、URL 输入2、DNS 域名解析3、建立 TCP 连接三次握手概念三次握手理解 4、发送 HTTP/HTTPS 请求5、服务器处理,并返回响应6、浏览器解析并渲染页面7、请求结束,端口 TCP 连接四次挥手概念四次挥手理解 1、URL 输入 2、DNS 域名解析…...
【kubernetes】关于k8s集群如何将pod调度到指定node节点?
目录 一、k8s的watch机制 二、scheduler的调度策略 Predicate(预选策略) 常见算法: priorities(优选策略)常见的算法有: 三、k8s的标签管理之增删改查 四、k8s的将pod调度到指定node的方法 方案一&am…...
【框架】React和Vue的异同
1. 前言 React对于原生JS要求会高一级,国外React用的多,国内Vue用的多。 2. 共同点 组件化函数式编程 (vue3函数式编程、vue2声明式编程)单向数据流,数据驱动视图VirtualDOM Diff算法操作DOM社区成熟,…...
如何选择阅读软件技术学习书籍
如何选择阅读软件技术学习书籍 这里以软件技术学习的角度结合自身感悟谈谈,如何选择阅读书籍。 人的时间和精力都是非常有限的,软件技术学习者如何选择阅读书籍。以下是从我的经验教训总结的一些体会: 1、确定自己的兴趣领域和阅读目标 选…...
OFA-VE环境部署:Python 3.11+PyTorch+CUDA一站式配置手册
OFA-VE环境部署:Python 3.11PyTorchCUDA一站式配置手册 1. 引言:认识OFA-VE视觉推理系统 OFA-VE是一个基于阿里巴巴达摩院OFA大模型构建的多模态推理平台,专门用于分析图像内容与文本描述之间的逻辑关系。这个系统采用了现代化的赛博朋克视…...
3分钟让Windows文件资源管理器焕然一新:ExplorerBlurMica毛玻璃效果完全指南
3分钟让Windows文件资源管理器焕然一新:ExplorerBlurMica毛玻璃效果完全指南 【免费下载链接】ExplorerBlurMica Add background Blur effect or Acrylic (Mica for win11) effect to explorer for win10 and win11 项目地址: https://gitcode.com/gh_mirrors/ex/…...
突破平台限制:WorkshopDL重构Steam创意工坊资源获取体验
突破平台限制:WorkshopDL重构Steam创意工坊资源获取体验 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL WorkshopDL作为一款仅10MB大小的开源工具,通过智…...
YOLOFuse实战案例:如何利用红外+RGB融合提升森林火情监测精度
YOLOFuse实战案例:如何利用红外RGB融合提升森林火情监测精度 1. 森林火情监测的痛点与挑战 森林火灾是全球性的生态灾难,每年造成巨大经济损失和生态破坏。传统监测手段主要依赖可见光摄像头和人工巡查,存在明显局限性: 夜间失…...
3步解锁B站4K视频:bilibili-downloader零基础使用指南
3步解锁B站4K视频:bilibili-downloader零基础使用指南 【免费下载链接】bilibili-downloader B站视频下载,支持下载大会员清晰度4K,持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader 还在为无法保存B站4…...
从零开始:用CosyVoice2-0.5B快速搭建AI语音生成平台
从零开始:用CosyVoice2-0.5B快速搭建AI语音生成平台 1. 为什么选择CosyVoice2-0.5B? 语音合成技术已经发展多年,但大多数解决方案要么需要复杂的配置过程,要么需要大量训练数据。阿里开源的CosyVoice2-0.5B打破了这一局面&#…...
SENet实战:如何在PyTorch中实现Squeeze-and-Excitation模块(附完整代码)
PyTorch实战:手把手实现SENet中的SE模块 在计算机视觉领域,注意力机制已经成为提升模型性能的重要工具。今天我们将深入探讨如何在PyTorch中实现Squeeze-and-Excitation(SE)模块——这个让ResNet-50在ImageNet上表现接近ResNet-10…...
MDXEditor指令系统详解:如何扩展Markdown语法
MDXEditor指令系统详解:如何扩展Markdown语法 【免费下载链接】editor A rich text editor React component for markdown 项目地址: https://gitcode.com/gh_mirrors/editor/editor MDXEditor是一个功能丰富的React组件,专为Markdown编辑设计&am…...
软件实施交付转运维学习第三天:Linux系统命令基础(部分)
从实施到运维的蜕变之路,掌握命令就是掌握Linux的灵魂写在前面作为一名从软件实施交付转向运维的工程师,我深刻体会到:Linux命令不仅仅是简单的指令,更是与操作系统对话的语言。当我们站在实施和运维的交界处,掌握Linu…...
OpenClaw人人养虾:配置Anthropic (Claude)
Anthropic 是 Claude 系列模型的开发者。Claude 以出色的指令遵循能力、深度推理和长文本处理著称。OpenClaw 支持通过 API Key 或 Claude Code CLI OAuth 接入。 认证方式 方式一:API Key(推荐) 前往 Anthropic Console 创建 API Key在 O…...
