Talk is cheap. Show me your code

前端生成二维码并批量打包成zip文件

最近接到一个需求,需要在列表上显示二维码图片,用户还可以勾选多个二维码图片,打包为 zip 文件下载

将 URL 转为二维码图片并不复杂,qrcodejsnode-qrcode 都是很成熟的方案

打包 zip 听起来很恐怖,但用上 jszip 就很简单了

这篇文章介绍的是 node-qrcode + jszip 的解决方案。前端框架用的是 React,不过核心逻辑是通用的

 

一、生成二维码

首先安装 node-qrcode

yarn add qrcode @types/qrcode

在浏览器环境下,node-qrcode 提供了这几个 API:toCanvas()toDataURL()toString()

推荐使用 toDataURL, 然后直接将路径作为 src 用于 <img /> 标签

还可以出传入一些配置项 Option 来设置二维码图片的颜色、格式等

import QRCode from 'qrcode';

const opts: QRCode.QRCodeToDataURLOptions = {
  errorCorrectionLevel: 'H',
  type: 'image/jpeg',
  margin: 1,
  color: {
    dark:"#010599FF",
    light:"#FFBF60FF"
  }
}

QRCode.toDataURL('text', opts).then(url => {
  const img = document.getElementById('image')
  img.src = url
})

基于以上 API,可以封装一个简单的二维码组件:

import React, { useEffect, useState } from 'react';
import QRCode from 'qrcode';
import type { QRCodeToDataURLOptions } from 'qrcode';

export type QrcodeImgProps = {
  url: string;
  options?: QRCodeToDataURLOptions;
};

/** 二维码图片组件  */
export const QrcodeImg: React.FC<QrcodeImgProps> = ({ url, options }) => {
  const [src, setSrc] = useState<string>('');

  useEffect(() => {
    QRCode.toDataURL(url, options)
      .then((x) => {
        setSrc(x);
      })
      .catch(() => {
        setSrc('');
        console.error('生成二维码失败');
      });
  }, [url, options]);

  return src ? <img className="qrcode" src={src} /> : null;
};

export default QrcodeImg;

 

二、批量导出 ZIP

安装 jszip

yarn add jszip

基本用法如下:

const zip = new JSZip();

// 直接压缩文件
zip.file("Hello.txt", "Hello World\n");

// 创建文件夹
const img = zip.folder("images");
// 创建图片并放进文件夹
img.file("smile.gif", imgData, {base64: true});

// 生成 zip 文件
zip.generateAsync({type:"blob"}).then(function(content) {
    // content 为 'application/zip' 文件流,需要手动下载
});

jszip 可以基于 base64 创建图片文件,而上面通过 qrcode 生成的是一个 dataUrl,所以需要转换一下:

/** 从 dataUrl 截取 Base64 字符串*/
function dataUrlToBase64(dataUrl: string) {
  if (typeof dataUrl !== 'string') return '';
  const idx = dataUrl.indexOf('base64,') + 'base64,'.length;
  return dataUrl.substring(idx);
}

最后一个批量导出二维码的函数就出来了:

import QRCode from "qrcode";
import JSZip from "jszip";

export type QrcodeItem = {
  /** 二维码标题 */
  name: string;
  /** 二维码地址 */
  url: string;
};

/**
 * 批量下载二维码
 * @param data 二维码数据集
 * @param name 打包后的文件名
 */
export function downloadBatchQrcodeImg(data: QrcodeItem[], name?: string) {
  if (!Array.isArray(data) || !data.length) {
    return console.error('二维码数据异常');
  }
  const zipName = name || '未命名';
  const zip = new JSZip();
  const folder = zip.folder(zipName);
  // 批量创建二维码
  Promise.all(data.map((item) => QRCode.toDataURL(item.url))).then((values) => {
    values.forEach((v, index) => {
      const content = dataUrlToBase64(v);
      // qrcode 默认生成 png 图片
      folder?.file(`${data[index].name}.png`, content, { base64: true });
    });
    zip.generateAsync({ type: 'blob' }).then(function (content) {
      // 下载文件
      downloadBlobFile(content, `${zipName}.zip`, 'application/zip');
    });
  });
}

最后贴一下 downloadBlobFile 函数,是一个比较粗糙的下载文件方法,仅供参考

export type MsNavigator = {
  msSaveOrOpenBlob?: (blob: any, defaultName?: string) => boolean;
};

/** 下载文件流, 默认导出 Excel */
export default function downloadBlobFile(
  data: Blob,
  fileName: string,
  type = 'application/vnd.ms-excel',
) {
  const Navigator = window.navigator as MsNavigator;
  if (Navigator?.msSaveOrOpenBlob) {
    Navigator.msSaveOrOpenBlob(data, fileName);
  } else {
    const aTag = document.createElement('a');
    // 获取 blob 本地文件连接 (blob 为纯二进制对象,不能够直接保存到磁盘上)
    const downUrl = window.URL.createObjectURL(new Blob([data], { type }));
    // 定义导出文件的命名
    aTag.href = downUrl;
    aTag.download = fileName;
    aTag.click();
    window.URL.revokeObjectURL(downUrl);
  }
}

 

 

posted @ 2023-02-13 10:08  Wise.Wrong  阅读(763)  评论(0编辑  收藏  举报