功能简介
- 上传 excel、图片
- 拖拽excel中的列字段到图片相应位置
- 最终得到包含 excel 数据的多个图片的压缩包
- 在合成图片过程中可以查看合成进度
- 在下载压缩包时,会调用七牛云的 mkzip 压缩多文件进行下载
功能所需技术点
-
node 服务利用 node-xlsx 解析 excel 文件,前端通过 xlsx 生成 excel 文件,所以实现下载 excel 文件有两种方式,
- 后端返回文件流,前端利用 a 标签 download 实现下载
- 后端返回 excel 数组数据,前端利用 xlsx 生成 excel文件实现下载
-
拖拽标签🏷到图片功能,利用 react-rnd 实现,监听
onDragStop,确定标签在图片上的位置并存储,供给之后合成图片使用 -
node 服务遍历excel数据,请求模板服务(此服务展示图片,并将字段显示为真实数据展示),当页面显示完成时,利用 puppeteer 中文文档
- 截图
const browser = await puppeteer.launch({headless: false}); const page = await browser.newPage() // 设置截图宽高 puppeteerPage.setViewport({ width, height }) await puppeteerPage.goto(url, { waitUntil: 'networkidle0' }) // 截图到指定位置 await puppeteerPage.screenshot({ path: filePath })- 存储的过程需要读本地图片上传至七牛云
const imgStream = fs.createReadStream(filePath) // 利用七牛云 api 实现上传- 存储生成图片并上传的进度信息,将图片地址,表格数据id,模板id 都存储
-
合成进度的查看,根据记录id分别查询合成成功和失败的条数,(成功+失败)/总条数为进度信息,前端使用 EventSource
- SSE 与 WebSocket 作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。
- 总体来说,WebSocket 更强大和灵活。因为它是全双工通道,可以双向通信;SSE 是单向通道,只能服务器向浏览器发送,因为流信息本质上就是下载。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。
const source = new EventSource(url) source.onopen = () => { console.log('成功连接服务器') } source.onmessage = (event) => { // 监听未命名事件 dispatch(setList(JSON.parse(event.data))) } // 当组件卸载时,关闭 source.close()服务端
ctx.res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive', }) // 每6秒向客户端推送一次数据 ctx.res.write(`retry:6000\ndata: ${JSON.stringify(data)}\n\n`) -
下载压缩包,利用七牛云mkzip,多文件压缩 node.js sdk持久化数据处理,实现参考,请求七牛云文件压缩后,返回
persistentId,然后前端使用 Web Worker 阮一峰 开启定时器轮询持久化任务状态接口,code 返回 0 时去请求下载export function createWorker(f, param) { const blob = new Blob([`(${f.toString()})(persistentId=${JSON.stringify(param)})`]) const url = window.URL.createObjectURL(blob) const worker = new Worker(url) return worker } // 开启一个子线程 const pollingWorker = createWorker((id) => { setInterval(() => { // 这里一个坑,url 必须写全地址 fetch(`${location.origin}/qiniuapi/status/get/prefop?id=${id}`) .then((res) => res.json()).then(({ code }) => { // 这里将请求到的结果发给主线程 self.postMessage(code) }) }, 2000) }, persistentId) pollingWorker.onmessage = (e) => { console.log('onmessage', e.data) // 主线程获取到的数据为 0 时,开始下载,下载完后关闭 worker 子线程 if (e.data === 0) { const a = document.createElement('a') a.href = link a.download = '下载' document.body.appendChild(a) a.click() document.body.removeChild(a) pollingWorker.terminate() } else if (e.data > 2) { message.error('下载失败,请重试') } } pollingWorker.postMessage('init')
实现效果
- 拖动列字段到图片模板
- 进度信息用户无感自动刷新,用户不需要任何操作,进度即可自动刷新
- 点击下载按钮会等待一段时间,然后进行附件下载