java 生成、直接/批量下载二维码

621 阅读3分钟

前言

二维码应用到生活的各个方面,会用代码实现二维码,我想一定是一项加分的技能。好了,我们来一起实现一下吧。

我们实现的二维码是基于QR Code的标准的,QR Code是由日本Denso公司于1994年研制的一种矩阵二维码符号码,全称是Quick Response Code

QR Code:专利公开,支持中文;

QR Code与其他二维码相比,具有识读速度快、数据密度大、占用空间小的优势;

效果图

image.png

代码实现

maven 依赖

使用的是zxing所以需要2个依赖,下面是pom.xml的配置:

  <!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
      <groupId>com.google.zxing</groupId>           
      <artifactId>core</artifactId>            
      <version>3.2.1</version>       
</dependency>        
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->        <dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.2.1</version>
</dependency>

工具类编写(QRCodeUtil)

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Objects;

// 生成二维码工具类
public class QRCodeUtil {

    //文字显示
    private static final int QRCOLOR = 0x201f1f; // 二维码颜色:黑色
    private static final int BGWHITE = 0xFFFFFF; //二维码背景颜色:白色
    // 1、设置二维码的一些参数
    private static HashMap hints = new HashMap();

    static {
        // 1.1设置字符集
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");

        // 1.2设置容错等级;因为有了容错,在一定范围内可以把二维码p成你喜欢的样式
        /**
         *  纠错能力:
         *  L级:约可纠错7%的数据码字
         *  M级:约可纠错15%的数据码字
         *  Q级:约可纠错25%的数据码字
         *  H级:约可纠错30%的数据码字
         */
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

        // 1.3设置外边距;(即白色区域)
        hints.put(EncodeHintType.MARGIN, 1);
    }

    /**
     * 基础生成二维码的方法,不带 logo
     *
     * @param width   二维码的宽
     * @param height  二维码的高
     * @param content 二维码的内容
     */
    public static BufferedImage createQrCode(int width, int height, String content) {
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            return MatrixToImageWriter.toBufferedImage(bitMatrix);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成携带logo的二维码
     *
     * @param width
     * @param height
     * @param content
     * @param logoFile logo 路径
     */
    public static BufferedImage createLogoQrCode(int width, int height, String content, File logoFile) {
        try {
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            // 参数顺序分别为: 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
            BitMatrix bm = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

            // 开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF) 白(0xFF000000)两色
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGWHITE);
                }
            }

            addLogo(image, logoFile);
            return image;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @param image   二维码图像。
     * @param logoPic logo文件路径
     */
    private static void addLogo(BufferedImage image, File logoPic) {
        try {
            // 1、读取二维码图片,并构建绘图对象
            Graphics2D graph = image.createGraphics();

            // 2、读取logo图片
            BufferedImage logo = ImageIO.read(logoPic);

            //  大小
            int width = image.getWidth();
            int height = image.getHeight();
            int widthLogo = width / 5;
            int heightLogo = height / 5;

            // 3、计算图片放置的位置
            int x = (width - widthLogo) / 2;
            int y = (height - heightLogo) / 2;

            if (false) {  // 是否添加白底
                int fillWidth = widthLogo + 15;
                int fillHeight = heightLogo + 15;
                int fillX = (width - fillWidth) / 2;
                int fillY = (height - fillHeight) / 2;
                graph.fillRect(fillX, fillY, fillWidth, fillHeight);
            }
            // 4、绘制图片
            graph.drawImage(logo, x, y, widthLogo, heightLogo, null);
            // 5、释放它正在使用的系统资源
            graph.dispose();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 添加背景图、文字描述
     * 如果X轴==null 则居中显示
     * 如果Y轴==null 则居中显示
     * 文字Y轴为null文字默认在二维码上方
     */
    public static BufferedImage addBackground(BufferedImage qrImage, File bgFile, String text, Integer qrX, Integer qrY, Integer textX, Integer textY) {
        try {
            // 读取背景图片,并构建绘图对象
            BufferedImage image = ImageIO.read(bgFile);
            Graphics2D graph = image.createGraphics();
            int bgWidth = image.getWidth();
            int bgHeight = image.getHeight();

            int width = qrImage.getWidth();
            int height = qrImage.getHeight();

            // 计算二维码图片放置的位置
            if (Objects.isNull(qrX)) {
                qrX = (bgWidth - width) / 2;
            }
            if (Objects.isNull(qrY)) {
                qrY = (bgHeight - height) / 2;
            }

            // 绘制图片
            graph.drawImage(qrImage, qrX, qrY, width, height, null);

            //文字描述参数1
            //设置字体类型和大小(BOLD加粗/ PLAIN平常)
            graph.setFont(new Font("宋体", Font.PLAIN, 20));
            //设置字体颜色
            graph.setColor(Color.white);
            int strWidth = graph.getFontMetrics().stringWidth(text);

            //文字1显示位置
            if (Objects.isNull(textX)) {
                textX = (bgWidth - strWidth) / 2;
            }
            if (Objects.isNull(textY)) {
                textY = qrY - 20;
            }
            // 绘制文字
            graph.drawString(text, textX, textY);//上下

            return image;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

后端下载代码(DownloadController)

@RequestMapping("/download")
public void download(String fileName, String content, HttpServletResponse response) throws Exception {
      File logoFile = new File("C:\\Users\\Administrator\\Desktop/avatar.png");
      BufferedImage qrImage = QRCodeUtil.createLogoQrCode(250, 250, "hello,world", logoFile);
       
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      ImageIO.write(qrImage, "png", byteArrayOutputStream);
      //设置response
      response.setContentType("application/x-msdownload");

      String downloadFilename = URLEncoder.encode("二维码文件名", "UTF-8");
      response.setHeader("Content-Disposition", "attachment;filename=" + downloadFilename + ".png");
      //输出流
      response.getOutputStream().write(byteArrayOutputStream.toByteArray());
}

前端下载代码

<a href="/download/?fileName=联通">直接下载</a>

打包/批量下载

后端代码

 /**
 *
 * 生成二维码下载压缩包
 */
@RequestMapping("/downloadZip")
public void downloadZip(@RequestBody List<Map<String,String>> list, HttpServletResponse response) throws Exception {
    List<BufferedImage> images = new ArrayList<>();
    for (Map<String,String> item : list) {
        BufferedImage image = QRCodeUtil.createLogoQrCode(250, 250, "hello", new File("C:\\Users\\Administrator\\Desktop/test.png"));
        images.add(image);
    }

    // 指明response的返回对象是文件流
    response.setContentType("application/octet-stream; charset=UTF-8");
    // 设置在下载框默认显示的文件名
    String downloadFilename = URLEncoder.encode("二维码", "UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename=" + downloadFilename + ".zip");

    ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
    BufferedImage[] files = new BufferedImage[images.size()];
    images.toArray(files);
    for (int i = 0; i < files.length; i++) {
        BufferedImage img = files[i];
        zos.putNextEntry(new ZipEntry("temp_download" + File.separator + list.get(i).get("name") + ".jpg"));
        ImageIO.write(img, "png", zos);
    }
    zos.flush();
    zos.close();
}

前端代码

 // 创建一个异步请求的对象
var xhr = new XMLHttpRequest();
// 设置使用 Post 方法进行提交,并且设置请求的 url
xhr.open("POST", "/downloadZip");
// 设置发送数据的格式为JSON
xhr.setRequestHeader("Content-Type", "application/json");
// 注册事件 (委托浏览器在请求结束的时候触发这个逻辑)
xhr.onload = function (e) {
   var blob = xhr.response;
   var a = document.createElement('a');
   a.download = '打包好的.zip';
   a.href=window.URL.createObjectURL(blob);
   a.click();
};
// 开始发送请求!
var params =  JSON.stringify([{name:'aaa'}])
xhr.send(params);