前端生成二維碼並批量打包成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);
  }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章