二维码工具类

/ Java / 没有评论 / 570浏览

效果图

alt

代码

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.mzlion.easyokhttp.HttpClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Hashtable;

/**
 * 二维码工具类
 *
 * @author zxk175
 * @date 2018/3/24 16:20
 * @link https://blog.csdn.net/mrwangxsyz/article/details/52447323
 */
@Slf4j
public class QrCodeUtils {

    private static final String CHARSET = "UTF-8";
    private static final String FORMAT_NAME = "png";
    /**
     * 二维码宽度
     */
    private static final int WIDTH = 420;
    /**
     * 二维码高度
     */
    private static final int HEIGHT = 420;
    /**
     * Logo尺寸
     */
    private static final int LOGO_SIZE = 100;
    /**
     * 圆角弧度
     */
    private static final int RADIUS = 20;


    public static void main(String[] args) throws Exception {
        String imageBase641 = imageToBase64("你好 世界!", Const.DEFAULT_AVATAR, true, true);
        String imageBase642 = imageToBase64("你好 世界!", "img/logo.png", false, true);
        base64ToImage(imageBase641);
        base64ToImage(imageBase642);
    }

    /**
     * base64转图片
     *
     * @param base64String
     * @throws Exception
     */
    private static void base64ToImage(String base64String) throws Exception {
        byte[] bytes = Base64.decode(base64String);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        BufferedImage bi = ImageIO.read(byteArrayInputStream);
        String file = System.currentTimeMillis() + "." + FORMAT_NAME;
        ImageIO.write(bi, FORMAT_NAME, new File("/Users/zxk175/" + file));
    }

    /**
     * 图片转base64
     *
     * @param content
     * @param logoPath
     * @param needCompress
     * @return
     * @throws Exception
     */
    public static String imageToBase64(String content, String logoPath, boolean isRemote, boolean needCompress) throws Exception {
        Color topLeftColor = new Color(231, 144, 56);
        Color topRightColor = new Color(231, 144, 56);
        Color bottomLeftColor = new Color(50, 165, 162);

        BufferedImage image = createQrImage(content, logoPath, isRemote, needCompress, topLeftColor, topRightColor, bottomLeftColor, true);

        // 转Base64
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(image, FORMAT_NAME, byteArrayOutputStream);
        byte[] bytes = byteArrayOutputStream.toByteArray();
        return Base64.encode(bytes).trim();
    }

    /**
     * 生成二维码
     *
     * @param content
     * @param logoPath
     * @param isRemote
     * @param needCompress
     * @param topLeftColor
     * @param topRightColor
     * @param bottomLeftColor
     * @param all
     * @return
     * @throws Exception
     */
    private static BufferedImage createQrImage(String content,
                                               String logoPath,
                                               boolean isRemote,
                                               boolean needCompress,
                                               Color topLeftColor,
                                               Color topRightColor,
                                               Color bottomLeftColor,
                                               boolean all) throws Exception {

        // 码眼颜色覆盖
        int startModel = 2;
        int endModel = 5;
        if (all) {
            startModel = 0;
            endModel = 7;
        }

        Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
        // 指定纠错等级,纠错级别(L 7%、M 15%、Q 25%、H 30%)
        ErrorCorrectionLevel level = ErrorCorrectionLevel.H;
        hints.put(EncodeHintType.ERROR_CORRECTION, level);
        // 内容所使用字符集编码
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        // 设置二维码边的空度,非负数
        hints.put(EncodeHintType.MARGIN, 2);

        // 生成二维码
        BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);

        // 获取二维码宽高
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        int[] pixels = new int[width * height];

        int[] topLeft = bitMatrix.getTopLeftOnBit();
        int version = version(level, content.getBytes(CHARSET).length);
        // 获取单边模块数
        int totalModelNum = (version - 1) * 4 + 5 + 16;
        int resultWidth = width - 2 * (topLeft[0]);
        // 得到每个模块长度
        int modelWidth = resultWidth / totalModelNum;
        // 得到三个基准点的起始与终点
        int topStartX = topLeft[0] + modelWidth * startModel;
        int topEndX = topLeft[0] + modelWidth * endModel;

        int topStartY = topLeft[0] + modelWidth * startModel;
        int topEndY = topLeft[0] + modelWidth * endModel;

        int rightStartX = (totalModelNum - endModel) * modelWidth + topLeft[0];
        int rightEndX = width - modelWidth * startModel - topLeft[0];

        int leftStartY = height - modelWidth * endModel - topLeft[1];
        int leftEndY = height - modelWidth * startModel - topLeft[1];

        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                if (bitMatrix.get(x, y)) {
                    if (x >= topStartX && x < topEndX && y >= topStartY && y < topEndY) {
                        // 左上角颜色
                        pixels[y * width + x] = topLeftColor.getRGB();
                    } else if (x < rightEndX && x >= rightStartX && y >= topStartY && y < topEndY) {
                        // 右上角颜色
                        pixels[y * width + x] = topRightColor.getRGB();
                    } else if (x >= topStartX - 2 && x < topEndX && y >= leftStartY - 2 && y < leftEndY) {
                        // 右下角颜色
                        pixels[y * width + x] = bottomLeftColor.getRGB();
                    } else {
                        // 二维码内容颜色
                        int num1 = (int) (50 - (50.0 - 13.0) / height * (y + 1));
                        int num2 = (int) (165 - (165.0 - 72.0) / height * (y + 1));
                        int num3 = (int) (162 - (162.0 - 107.0) / height * (y + 1));
                        Color color = new Color(num1, num2, num3);
                        pixels[y * width + x] = color.getRGB();
                    }
                } else {
                    // 背景颜色
                    pixels[y * width + x] = Color.WHITE.getRGB();
                }
            }
        }

        // 绘制二维码内容
        image.getRaster().setDataElements(0, 0, width, height, pixels);

        // 获取 logo 文件流
        InputStream netPic2Stream;
        if (isRemote) {
            netPic2Stream = getNetPic2Stream(logoPath);
        } else {
            ClassPathResource classPathResource = new ClassPathResource(logoPath);
            netPic2Stream = classPathResource.getInputStream();
        }
        addLogo(image, netPic2Stream, needCompress);
        return image;
    }

    /**
     * 插入Logo
     *
     * @param source       二维码图片
     * @param logoIn       Logo图片流
     * @param needCompress 是否压缩
     * @throws Exception
     */
    private static void addLogo(BufferedImage source, InputStream logoIn, boolean needCompress) throws Exception {
        if (ObjectUtil.isNull(logoIn)) {
            throw new RuntimeException("Logo图片流为空");
        }

        BufferedImage logoImage = ImageIO.read(logoIn);
        int width = logoImage.getWidth();
        int height = logoImage.getHeight();

        // 压缩logo
        if (needCompress) {
            if (width > LOGO_SIZE) {
                width = LOGO_SIZE;
            }
            if (height > LOGO_SIZE) {
                height = LOGO_SIZE;
            }

            BufferedImage tmpImage = logoImage;
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            Graphics graphics = tag.getGraphics();
            // 绘制缩小后的图
            graphics.drawImage(tmpImage, 0, 0, null);
            graphics.dispose();

            logoImage = tmpImage;
        }

        Graphics2D graph = source.createGraphics();
        graph.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graph.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graph.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        graph.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);

        // 设置logo图片放置位置
        // 中心
        int x = (WIDTH - width) / 2;
        int y = (HEIGHT - height) / 2;

        // 右下角
        //int x = (WIDTH - width);
        //int y = (HEIGHT - height);

        // 开始绘制Logo图片
        graph.drawImage(logoImage, x, y, width, height, null);
        BasicStroke stroke = new BasicStroke(5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        // 设置笔画对象
        graph.setStroke(stroke);
        // 指定弧度的圆角矩形
        RoundRectangle2D.Float round = new RoundRectangle2D.Float(x, y, width, height, RADIUS, RADIUS);
        graph.setColor(new Color(255, 255, 255));
        // 绘制圆弧矩形
        graph.draw(round);

        graph.dispose();
    }

    /**
     * 获取网络图片流
     *
     * @param url
     * @return
     */
    private static InputStream getNetPic2Stream(String url) {
        byte[] bytes = HttpClient.get(url).asByteData();
        return new ByteArrayInputStream(bytes);
    }

    /**
     * 二维码版本
     *
     * @param level
     * @param dataBitLength
     * @return
     * @link http://www.qrcode.com/en/about/version.html
     */
    private static int version(ErrorCorrectionLevel level, int dataBitLength) {
        dataBitLength = dataBitLength * 8;
        int bits = level.getBits();

        switch (ErrorCorrectionLevel.forBits(bits)) {
            case L: {
                if (dataBitLength <= 144 - 9) {
                    return 1;
                } else if (dataBitLength <= 272 - 9) {
                    return 2;
                } else if (dataBitLength <= 440 - 9) {
                    return 3;
                } else if (dataBitLength <= 640 - 9) {
                    return 4;
                } else if (dataBitLength <= 864 - 9) {
                    return 5;
                } else if (dataBitLength <= 1088 - 9) {
                    return 6;
                } else if (dataBitLength <= 1248 - 9) {
                    return 7;
                } else if (dataBitLength <= 1552 - 9) {
                    return 8;
                } else if (dataBitLength <= 1856 - 9) {
                    return 9;
                } else if (dataBitLength <= 2192 - 17) {
                    return 10;
                } else if (dataBitLength <= 2592 - 17) {
                    return 11;
                } else if (dataBitLength <= 2960 - 17) {
                    return 12;
                } else if (dataBitLength <= 3424 - 17) {
                    return 13;
                } else if (dataBitLength <= 3688 - 17) {
                    return 14;
                } else if (dataBitLength <= 4184 - 17) {
                    return 15;
                } else if (dataBitLength <= 4712 - 17) {
                    return 16;
                } else if (dataBitLength <= 5176 - 17) {
                    return 17;
                } else if (dataBitLength <= 5768 - 17) {
                    return 18;
                } else if (dataBitLength <= 6360 - 17) {
                    return 19;
                } else if (dataBitLength <= 6888 - 17) {
                    return 20;
                } else {
                    return 0;
                }
            }
            case M: {
                if (dataBitLength <= 128 - 9) {
                    return 1;
                } else if (dataBitLength <= 224 - 9) {
                    return 2;
                } else if (dataBitLength <= 352 - 9) {
                    return 3;
                } else if (dataBitLength <= 512 - 9) {
                    return 4;
                } else if (dataBitLength <= 688 - 9) {
                    return 5;
                } else if (dataBitLength <= 864 - 9) {
                    return 6;
                } else if (dataBitLength <= 992 - 9) {
                    return 7;
                } else if (dataBitLength <= 1232 - 9) {
                    return 8;
                } else if (dataBitLength <= 1456 - 9) {
                    return 9;
                } else if (dataBitLength <= 1728 - 17) {
                    return 10;
                } else if (dataBitLength <= 2032 - 17) {
                    return 11;
                } else if (dataBitLength <= 2320 - 17) {
                    return 12;
                } else if (dataBitLength <= 2672 - 17) {
                    return 13;
                } else if (dataBitLength <= 2920 - 17) {
                    return 14;
                } else if (dataBitLength <= 3320 - 17) {
                    return 15;
                } else if (dataBitLength <= 3624 - 17) {
                    return 16;
                } else if (dataBitLength <= 4056 - 17) {
                    return 17;
                } else if (dataBitLength <= 4504 - 17) {
                    return 18;
                } else if (dataBitLength <= 5016 - 17) {
                    return 19;
                } else if (dataBitLength <= 5352 - 17) {
                    return 20;
                } else {
                    return 0;
                }
            }
            case Q: {
                if (dataBitLength <= 104 - 9) {
                    return 1;
                } else if (dataBitLength <= 176 - 9) {
                    return 2;
                } else if (dataBitLength <= 272 - 9) {
                    return 3;
                } else if (dataBitLength <= 384 - 9) {
                    return 4;
                } else if (dataBitLength <= 496 - 9) {
                    return 5;
                } else if (dataBitLength <= 608 - 9) {
                    return 6;
                } else if (dataBitLength <= 704 - 9) {
                    return 7;
                } else if (dataBitLength <= 880 - 9) {
                    return 8;
                } else if (dataBitLength <= 1056 - 9) {
                    return 9;
                } else if (dataBitLength <= 1232 - 17) {
                    return 10;
                } else if (dataBitLength <= 1440 - 17) {
                    return 11;
                } else if (dataBitLength <= 1648 - 17) {
                    return 12;
                } else if (dataBitLength <= 1952 - 17) {
                    return 13;
                } else if (dataBitLength <= 2088 - 17) {
                    return 14;
                } else if (dataBitLength <= 2360 - 17) {
                    return 15;
                } else if (dataBitLength <= 2600 - 17) {
                    return 16;
                } else if (dataBitLength <= 2936 - 17) {
                    return 17;
                } else if (dataBitLength <= 3176 - 17) {
                    return 18;
                } else if (dataBitLength <= 3560 - 17) {
                    return 19;
                } else if (dataBitLength <= 3880 - 17) {
                    return 20;
                } else {
                    return 0;
                }
            }
            case H: {
                if (dataBitLength <= 72 - 9) {
                    return 1;
                } else if (dataBitLength <= 128 - 9) {
                    return 2;
                } else if (dataBitLength <= 208 - 9) {
                    return 3;
                } else if (dataBitLength <= 288 - 9) {
                    return 4;
                } else if (dataBitLength <= 368 - 9) {
                    return 5;
                } else if (dataBitLength <= 480 - 9) {
                    return 6;
                } else if (dataBitLength <= 528 - 9) {
                    return 7;
                } else if (dataBitLength <= 688 - 9) {
                    return 8;
                } else if (dataBitLength <= 800 - 9) {
                    return 9;
                } else if (dataBitLength <= 976 - 17) {
                    return 10;
                } else if (dataBitLength <= 1120 - 17) {
                    return 11;
                } else if (dataBitLength <= 1264 - 17) {
                    return 12;
                } else if (dataBitLength <= 1440 - 17) {
                    return 13;
                } else if (dataBitLength <= 1576 - 17) {
                    return 14;
                } else if (dataBitLength <= 1784 - 17) {
                    return 15;
                } else if (dataBitLength <= 2024 - 17) {
                    return 16;
                } else if (dataBitLength <= 2264 - 17) {
                    return 17;
                } else if (dataBitLength <= 2504 - 17) {
                    return 18;
                } else if (dataBitLength <= 2728 - 17) {
                    return 19;
                } else if (dataBitLength <= 3080 - 17) {
                    return 20;
                } else if (dataBitLength <= 3248 - 25) {
                    return 21;
                } else if (dataBitLength <= 3536 - 25) {
                    return 22;
                } else if (dataBitLength <= 3712 - 25) {
                    return 23;
                } else if (dataBitLength <= 4112 - 25) {
                    return 24;
                } else if (dataBitLength <= 4304 - 25) {
                    return 25;
                } else if (dataBitLength <= 4768 - 25) {
                    return 26;
                } else if (dataBitLength <= 5024 - 25) {
                    return 27;
                } else if (dataBitLength <= 5288 - 25) {
                    return 28;
                } else if (dataBitLength <= 5608 - 25) {
                    return 29;
                } else if (dataBitLength <= 5960 - 25) {
                    return 30;
                } else if (dataBitLength <= 6344 - 33) {
                    return 31;
                } else if (dataBitLength <= 6760 - 33) {
                    return 32;
                } else if (dataBitLength <= 7208 - 33) {
                    return 33;
                } else if (dataBitLength <= 7688 - 33) {
                    return 34;
                } else if (dataBitLength <= 7888 - 33) {
                    return 35;
                } else if (dataBitLength <= 8432 - 33) {
                    return 36;
                } else if (dataBitLength <= 8768 - 33) {
                    return 37;
                } else if (dataBitLength <= 9136 - 33) {
                    return 38;
                } else if (dataBitLength <= 9776 - 33) {
                    return 39;
                } else if (dataBitLength <= 10208 - 33) {
                    return 40;
                } else {
                    return 0;
                }
            }
            default: {
                return 0;
            }
        }
    }

    /**
     * 构建内容
     *
     * @param key
     * @return
     */
    public static String buildContent(String key, String content) throws Exception {
        String url = StrUtil.EMPTY;
        if (key.equals(Const.QR_USER)) {
            url = Const.QR_USER_URL + "/";
        } else if (key.equals(Const.QR_STORE)) {
            url = Const.QR_STORE_URL + "/";
        } else if (key.equals(Const.QR_DOWN)) {
            url = Const.QR_DOWN_URL;
        }

        return RequestUtils.getRequestURL(RequestUtils.getRequest(), true) + url + (StrUtil.isBlank(content) ? StrUtil.EMPTY : CommonUtils.desEncode(key, content));
    }
}