zoukankan      html  css  js  c++  java
  • java 生成二维码、可带LOGO、可去白边

    1.准备工作

      所需jar包:

    JDK 1.6:

      commons-codec-1.11.jar

      core-2.2.jar

      javase-2.2.jar

    JDK 1.7:

      commons-codec-1.11.jar

      core-3.2.1.jar

      javase-3.2.1.jar

    import java.awt.AlphaComposite;
    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.OutputStream;
    import java.util.HashMap;
    import java.util.Map;
    import javax.imageio.ImageIO;
    import org.apache.commons.codec.binary.Base64OutputStream;
    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;
    
    /**
     * 维码处理工具类
     * @explain
     * @author Marydon
     * @creationTime 2018年11月23日下午3:16:55
     * @version 2.0
     * @since 1.0
     * @email marydon20170307@163.com
     */
    public class QRcodeUtils {
       // base64编码集
        public static final String CHARSET = "UTF-8";
        // 二维码高度
        public static final int HEIGHT = 150;
        // 二维码宽度
        public static final int WIDTH = 150;
        // 二维码外边距
        public static final int MARGIN = 0;
        // 二维码图片格式
        private static final String FORMAT = "jpg";
    }  

    2.生成二维码

    /**
     * 生成二维码
     * @explain
     * @param data 字符串(二维码实际内容)
     * @return
     */
    public static BufferedImage createQRCode(String data) {
        return createQRCode(data, WIDTH, HEIGHT, MARGIN);
    }
     
    /**
     * 生成二维码
     * @explain
     * @param data 字符串(二维码实际内容)
     * @param width 宽
     * @param height 高
     * @param margin 外边距,单位:像素,只能为整数,否则:报错
     * @return BufferedImage
     */
    public static BufferedImage createQRCode(String data, int width, int height, int margin) {
        BitMatrix matrix;
        try {
            // 设置QR二维码参数
            Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(2);
            // 纠错级别(H为最高级别)
            // L级:约可纠错7%的数据码字
            // M级:约可纠错15%的数据码字
            // Q级:约可纠错25%的数据码字
            // H级:约可纠错30%的数据码字
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            // 字符集
            hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
            // 边框,(num * 10)
            hints.put(EncodeHintType.MARGIN, 0);// num
            // 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
            matrix = new MultiFormatWriter().encode(data, BarcodeFormat.QR_CODE,
                    width, height, hints);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return MatrixToImageWriter.toBufferedImage(matrix);
    }  

      说明:

      网上说,EncodeHintType.MARGIN的取值区间为[0,4],但是经过实际测试,当把它的值设为负整数、正整数的时候都不会报错,但不是能是小数;

      去白边,将EncodeHintType.MARGIN的值设为0的方法,根本无效,因为源码中并没有通过这个参数来设置白边的宽度;

      大致过程:先根据内容生成二维码,然后根据指定的宽高,对原来的二维码进行放大或缩小,白色边框的宽度=要求的宽高-放大或缩小后的二维码的宽高。

      comgooglezxingqrcodeQRCodeWriter.class源码解读

    private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone)
      {
        ByteMatrix input = code.getMatrix();
        if (input == null)
          throw new IllegalStateException();
    
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int qrWidth = inputWidth + quietZone * 2;
        int qrHeight = inputHeight + quietZone * 2;
        int outputWidth = Math.max(width, qrWidth);
        int outputHeight = Math.max(height, qrHeight);
    
        int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
        // 有白色边框的罪魁祸首:leftPadding和topPadding 
        int leftPadding = (outputWidth - inputWidth * multiple) / 2;
        int topPadding = (outputHeight - inputHeight * multiple) / 2;
    
        BitMatrix output = new BitMatrix(outputWidth, outputHeight);
    
        int inputY = 0; for (int outputY = topPadding; inputY < inputHeight; )
        {
          int inputX = 0; for (int outputX = leftPadding; inputX < inputWidth; ) {
            if (input.get(inputX, inputY) == 1)
              output.setRegion(outputX, outputY, multiple, multiple);
            ++inputX; outputX += multiple;
          }
          ++inputY; outputY += multiple;
        }
    
        return output;
      }  

    3.去白边

      在createQRCode()方法中添加如下代码:

    // 裁减白边(强制减掉白边)
    if (margin == 0) {
        bitMatrix = deleteWhite(bitMatrix);
    }
    

      具体实现方法:

    /**
     * 强制将白边去掉
     * @explain
     *  虽然生成二维码时,已经将margin的值设为了0,但是在实际生成二维码时有时候还是会生成白色的边框,边框的宽度为10px;
     *  白边的生成还与设定的二维码的宽、高及二维码内容的多少(内容越多,生成的二维码越密集)有关;
     *  因为是在生成二维码之后,才将白边裁掉,所以裁剪后的二维码(实际二维码的宽、高)肯定不是你想要的尺寸,只能自己一点点试喽!
     * @param matrix
     * @return 裁剪后的二维码(实际二维码的大小)
     */
    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;
    
        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1]))
                    resMatrix.set(i, j);
            }
        }
        
        int width = resMatrix.getWidth();
        int height = resMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, resMatrix.get(x, y) ? 0 : 255);// 0-黑色;255-白色
            }
        }
        
        return resMatrix;
    }

      说明:这种方法只能去除普通的二维码的白边,不能去除带LOGO的二维码的白边。

    4.生成带logo的二维码

    /**
     * 生成带logo的二维码
     * @explain 宽、高、外边距使用定义好的值
     * @param data 字符串(二维码实际内容)
     * @param logoFile logo图片文件对象
     * @return BufferedImage
     */
    public static BufferedImage createQRCodeWithLogo(String data, File logoFile) {
        return createQRCodeWithLogo(data, WIDTH, HEIGHT, MARGIN, logoFile);
    }
     
    /**
     * 生成带logo的二维码
     * @explain 自定义二维码的宽和高
     * @param data 字符串(二维码实际内容)
     * @param width 宽
     * @param height 高
     * @param logoFile logo图片文件对象
     * @return BufferedImage
     * @return
     */
    public static BufferedImage createQRCodeWithLogo(String data, int width, int height, int margin, File logoFile) {
        BufferedImage combined = null;
        try {
            BufferedImage qrcode = createQRCode(data, width, height, margin);
            BufferedImage logo = ImageIO.read(logoFile);
            int deltaHeight = height - logo.getHeight();
            int deltaWidth = width - logo.getWidth();
            combined = new BufferedImage(height, width, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = (Graphics2D) combined.getGraphics();
            g.drawImage(qrcode, 0, 0, null);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));
            g.drawImage(logo, (int) Math.round(deltaWidth / 2), (int) Math.round(deltaHeight / 2), null);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return combined;
    }  

    5.以什么样的方式返回二维码

    /**
     * 将二维码信息写入文件中
     * @explain
     * @param image
     * @param file 用于存储二维码的文件对象
     */
    public static void writeToFile(BufferedImage image, File file) {
        try {
            ImageIO.write(image, FORMAT, file);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    
    /**
     * 将二维码信息写入流中
     * @explain
     * @param image
     * @param 文件stream
     */
    public static void writeToStream(BufferedImage image, OutputStream stream) {
        try {
            ImageIO.write(image, FORMAT, stream);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    
    /**
     * 获取base64格式的二维码
     * @explain 图片类型:jpg
     *  展示:<img src="data:image/jpeg;base64,base64Str"/>
     * @param image
     * @return base64
     */
    public static String writeToString(BufferedImage image) {
        String base64Str;
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            OutputStream os = new Base64OutputStream(bos);
            writeToStream(image, os);
            // 按指定字符集进行转换并去除换行符
            base64Str = bos.toString(CHARSET).replace("
    ", "");
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return base64Str;
    }  

    6.base64生成图片

    /**
     * 将base64转成图片
     * @explain
     * @param base64 base64格式图片
     * @param file 用于存储二维码的文件对象
     */
    public static void base64ToImage(String base64, File file) {
    	FileOutputStream os;
    	try {
    		Base64 d = new Base64();
    		byte[] bs = d.decode(base64);
    		os = new FileOutputStream(file.getAbsolutePath());
    		os.write(bs);
    		os.close();
    	} catch (Exception e) {
    		throw new RuntimeException(e.getMessage(), e);
    	}
    }
    

    7.测试

      见文末推荐

    2018/11/29

      添加二维码边框参数设置;

      代码优化。

    2018/11/30

      添加去除白边功能。

  • 相关阅读:
    Kubernetes K8S之Helm部署、使用与示例
    Kubernetes K8S之鉴权RBAC详解
    多图详解Go的sync.Pool源码
    多图详解Go的互斥锁Mutex
    5.深入Istio源码:Pilot-agent作用及其源码分析
    ribbon源码分析
    nacos注册中心源码流程分析
    nacos配置中心使用
    基于nacos注册中心的ribbon定制规则
    golang调用百度音转文websocket服务“invalid frame type”错误排查及解决
  • 原文地址:https://www.cnblogs.com/Marydon20170307/p/9546411.html
Copyright © 2011-2022 走看看