对zxing的简单封装,记录一下

本封装采用建造者模式来开发,支持生成base64,文件。logo文件支持本地和线上。最后的调用是这样的。可以使用的前提是要依赖zxing。

String string = new QrCodeGenWrapper.Builder()
        .msg("123456")
        .logo("http://shop.szschina.cn/images/logo.png")
        .build()
        .asString();
System.out.println("=======string======="+string);

上图是表示生成了一个base64的图片

贴代码(生成类):

public class QrCodeGenWrapper {
    /**
     * 生成二维里面的内容
     */
    private String msg;
    /**
     * 二维码中间的logo
     */
    private String logo;
    /**
     * logo的样式, 目前支持圆角+普通
     */
    private LogoStyle logoStyle;
    /**
     * 生成二维码的宽
     */
    private Integer width;
    /**
     * 生成二维码的高
     */
    private Integer height;
    /**
     * 生成二维码的颜色
     */
    private MatrixToImageConfig matrixToImageConfig;
    /**
     * 识别度
     */
    private Map, Object> hints;
    /**
     * 0 - 4
     */
    private Integer padding;
    /**
     * 生成二维码图片的格式 png, jpg
     */
    private String picType;

    public QrCodeGenWrapper() {
        this(new Builder());
    }

    QrCodeGenWrapper(Builder builder) {
        this.logo = builder.logo;
        this.width = builder.width;
        this.height = builder.height;
        this.logoStyle = builder.logoStyle;
        this.hints = builder.hints;
        this.matrixToImageConfig = builder.matrixToImageConfig;
        this.picType = builder.picType;
        if (Strings.isNullOrEmpty(builder.msg)) {
            throw new NullPointerException("msg is not null");
        }
        this.msg = builder.msg;
    }

    public String msg() {
        return msg;
    }

    public String logo() {
        return logo;
    }

    public LogoStyle logoStyle() {
        return logoStyle;
    }

    public Integer width() {
        return width;
    }

    public Integer height() {
        return height;
    }

    public Integer padding() {
        return padding;
    }

    public MatrixToImageConfig matrixToImageConfig() {
        return matrixToImageConfig;
    }

    public Map, Object> hints() {
        return hints;
    }

    public String picType() {
        return picType;
    }

    public Builder newBuilder() {
        return new Builder(this);
    }

    private BufferedImage asBufferedImage() {
        BitMatrix bitMatrix = QrCodeUtil.encode(this);
        return QrCodeUtil.toBufferedImage(this, bitMatrix);
    }

    public String asString() {
        BufferedImage bufferedImage = asBufferedImage();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferedImage, this.picType(), outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64Util.encode(outputStream);
    }

    public boolean asFile(String absFileName) {
        File file = new File(absFileName);
        BufferedImage bufferedImage = asBufferedImage();
        try {
            if (!ImageIO.write(bufferedImage, this.picType(), file)) {
                throw new IOException("save qrcode image error!");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }


    public static class Builder {
        private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();
        /**
         * qrcode message's code, default UTF-8
         */
        private String code = "utf-8";
        private String msg;
        private String logo;
        private LogoStyle logoStyle = LogoStyle.ROUND;
        private Integer width;
        private Integer height;
        private MatrixToImageConfig matrixToImageConfig;
        private Map, Object> hints;
        private Integer padding;
        private String picType = "png";

        public Builder() {
            this.msg = "";
            this.logoStyle = LogoStyle.ROUND;
            this.width = 200;
            this.height = 200;
            this.logo = null;
            this.matrixToImageConfig = DEFAULT_CONFIG;
            this.padding = 1;
            Map, Object> hints = new HashMap<>(3);
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            hints.put(EncodeHintType.CHARACTER_SET, code);
            hints.put(EncodeHintType.MARGIN, this.padding);
            this.hints = hints;
        }

        Builder(QrCodeGenWrapper wrapper) {
            this.logo = wrapper.logo;
            this.width = wrapper.width;
            this.height = wrapper.height;
            this.logoStyle = wrapper.logoStyle;
            this.hints = wrapper.hints;
            this.matrixToImageConfig = wrapper.matrixToImageConfig;
            this.picType = wrapper.picType;
            if (Strings.isNullOrEmpty(wrapper.msg)) {
                throw new NullPointerException("msg is not null");
            }
            this.msg = wrapper.msg;
        }

        public Builder msg(String msg) {
            this.msg = msg;
            return this;
        }

        public Builder logo(String logo) {
            this.logo = logo;
            return this;
        }

        public Builder logoStyle(LogoStyle logoStyle) {
            this.logoStyle = logoStyle;
            return this;
        }

        public Builder width(Integer width) {
            this.width = width;
            return this;
        }

        public Builder height(Integer height) {
            this.height = height;
            return this;
        }

        public Builder matrixToImageConfig(MatrixToImageConfig matrixToImageConfig) {
            this.matrixToImageConfig = matrixToImageConfig;
            return this;
        }

        public Builder hints(Map, Object> hints) {
            this.hints = hints;
            return this;
        }

        public Builder padding(Integer padding) {
            this.padding = padding;
            return this;
        }

        public Builder picType(String picType) {
            this.picType = picType;
            return this;
        }

        public QrCodeGenWrapper build() {
            return new QrCodeGenWrapper(this);
        }
    }

}

生成二维码的工具类:

@Slf4j
public class QrCodeUtil {
    private static final int QUIET_ZONE_SIZE = 4;

    public static BitMatrix encode(QrCodeGenWrapper wrapper) {
        if (Strings.isNullOrEmpty(wrapper.msg())) {
            throw new NullPointerException("msg Is Not Null");
        }
        ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
        int quietZone = 1;
        if (wrapper.hints() != null) {
            if (wrapper.hints().containsKey(EncodeHintType.ERROR_CORRECTION)) {
                errorCorrectionLevel = ErrorCorrectionLevel.valueOf(wrapper.hints().get(EncodeHintType.ERROR_CORRECTION).toString());
            }
            if (wrapper.hints().containsKey(EncodeHintType.MARGIN)) {
                quietZone = Integer.parseInt(wrapper.hints().get(EncodeHintType.MARGIN).toString());
            }

            if (quietZone > QUIET_ZONE_SIZE) {
                quietZone = QUIET_ZONE_SIZE;
            } else if (quietZone < 0) {
                quietZone = 0;
            }
        }

        try {
            QRCode code = Encoder.encode(wrapper.msg(), errorCorrectionLevel, wrapper.hints());
            return renderResult(code, wrapper.width(), wrapper.height(), quietZone);
        } catch (WriterException e) {
            e.printStackTrace();
            return null;
        }
    }

    private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }

        // xxx 二维码宽高相等, 即 qrWidth == qrHeight
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int qrWidth = inputWidth + (quietZone * 2);
        int qrHeight = inputHeight + (quietZone * 2);


        // 白边过多时, 缩放
        int minSize = Math.min(width, height);
        int scale = calculateScale(qrWidth, minSize);
        if (scale > 0) {
            if (log.isDebugEnabled()) {
                log.debug("qrCode scale enable! scale: {}, qrSize:{}, expectSize:{}x{}", scale, qrWidth, width, height);
            }

            int padding, tmpValue;
            // 计算边框留白
            padding = (minSize - qrWidth * scale) / QUIET_ZONE_SIZE * quietZone;
            tmpValue = qrWidth * scale + padding;
            if (width == height) {
                width = tmpValue;
                height = tmpValue;
            } else if (width > height) {
                width = width * tmpValue / height;
                height = tmpValue;
            } else {
                height = height * tmpValue / width;
                width = tmpValue;
            }
        }

        int outputWidth = Math.max(width, qrWidth);
        int outputHeight = Math.max(height, qrHeight);

        int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
        int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
        int topPadding = (outputHeight - (inputHeight * multiple)) / 2;

        BitMatrix output = new BitMatrix(outputWidth, outputHeight);

        for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
            // Write the contents of this row of the barcode
            for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }
            }
        }

        return output;
    }

    /**
     * 如果留白超过15% , 则需要缩放
     * (15% 可以根据实际需要进行修改)
     *
     * @param qrCodeSize 二维码大小
     * @param expectSize 期望输出大小
     * @return 返回缩放比例, <= 0 则表示不缩放, 否则指定缩放参数
     */
    private static int calculateScale(int qrCodeSize, int expectSize) {
        if (qrCodeSize >= expectSize) {
            return 0;
        }

        int scale = expectSize / qrCodeSize;
        int abs = expectSize - scale * qrCodeSize;
        if (abs < expectSize * 0.15) {
            return 0;
        }

        return scale;
    }

    /**
     * 根据二维码配置 & 二维码矩阵生成二维码图片
     *
     * @param bitMatrix
     * @return
     * @throws IOException
     */
    public static BufferedImage toBufferedImage(QrCodeGenWrapper wrapper, BitMatrix bitMatrix) {
        int qrCodeWidth = bitMatrix.getWidth();
        int qrCodeHeight = bitMatrix.getHeight();
        BufferedImage qrCode = new BufferedImage(qrCodeWidth, qrCodeHeight, BufferedImage.TYPE_INT_RGB);

        for (int x = 0; x < qrCodeWidth; x++) {
            for (int y = 0; y < qrCodeHeight; y++) {
                qrCode.setRGB(x, y,
                        bitMatrix.get(x, y) ?
                                wrapper.matrixToImageConfig().getPixelOnColor() :
                                wrapper.matrixToImageConfig().getPixelOffColor());
            }
        }

        // 插入logo
        if (!(Strings.isNullOrEmpty(wrapper.logo()))) {
            try {
                ImageUtil.insertLogo(qrCode, wrapper.logo(), wrapper.logoStyle());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 若二维码的实际宽高和预期的宽高不一致, 则缩放
        int realQrCodeWidth = wrapper.width();
        int realQrCodeHeight = wrapper.height();
        if (qrCodeWidth != realQrCodeWidth || qrCodeHeight != realQrCodeHeight) {
            BufferedImage tmp = new BufferedImage(realQrCodeWidth, realQrCodeHeight, BufferedImage.TYPE_INT_RGB);
            tmp.getGraphics().drawImage(
                    qrCode.getScaledInstance(realQrCodeWidth, realQrCodeHeight,
                            Image.SCALE_SMOOTH), 0, 0, null);
            qrCode = tmp;
        }

        return qrCode;
    }

}

base64转换工具类:

public class Base64Util {
    public static String encode(ByteArrayOutputStream outputStream) {
        return Base64.getEncoder().encodeToString(outputStream.toByteArray());
    }
}

logo处理工具类:

public class ImageUtil {
    /**
     * 在图片中间,插入圆角的logo
     *
     * @param qrCode 原图
     * @param logo   logo地址
     * @throws IOException
     */
    public static void insertLogo(BufferedImage qrCode, String logo, LogoStyle logoStyle) throws IOException {
        int QRCODE_WIDTH = qrCode.getWidth();
        int QRCODE_HEIGHT = qrCode.getHeight();

        // 获取logo图片
        BufferedImage bf = getImageByPath(logo);
        int size = bf.getWidth() > QRCODE_WIDTH * 2 / 10 ? QRCODE_WIDTH * 2 / 50 : bf.getWidth() / 5;
        bf = ImageUtil.makeRoundBorder(bf, logoStyle, size, Color.WHITE); // 边距为二维码图片的1/10

        // logo的宽高
        int w = bf.getWidth() > QRCODE_WIDTH * 2 / 10 ? QRCODE_WIDTH * 2 / 10 : bf.getWidth();
        int h = bf.getHeight() > QRCODE_HEIGHT * 2 / 10 ? QRCODE_HEIGHT * 2 / 10 : bf.getHeight();

        // 插入LOGO
        Graphics2D graph = qrCode.createGraphics();

        int x = (QRCODE_WIDTH - w) / 2;
        int y = (QRCODE_HEIGHT - h) / 2;

        graph.drawImage(bf, x, y, w, h, null);
        graph.dispose();
        bf.flush();
    }


    /**
     * 根据路径获取图片
     *
     * @param path 本地路径 or 网络地址
     * @return 图片
     * @throws IOException
     */
    public static BufferedImage getImageByPath(String path) throws IOException {
        if (path.startsWith("http")) { // 从网络获取logo
            return ImageIO.read(new URL(path));
//            return ImageIO.read(HttpUtil.downFile(path));
        } else if (path.startsWith("/")) { // 绝对地址获取logo
            return ImageIO.read(new File(path));
        } else { // 从资源目录下获取logo
            return ImageIO.read(ImageUtil.class.getClassLoader().getResourceAsStream(path));
        }
    }


    /**
     * fixme 边框的计算需要根据最终生成logo图片的大小来定义,这样才不会出现不同的logo原图,导致边框不一致的问题
     * 

* 生成圆角图片 & 圆角边框 * * @param image 原图 * @param logoStyle 圆角的角度 * @param size 边框的边距 * @param color 边框的颜色 * @return 返回带边框的圆角图 */ public static BufferedImage makeRoundBorder(BufferedImage image, LogoStyle logoStyle, int size, Color color) { // 将图片变成圆角 int cornerRadius = 0; if (logoStyle == LogoStyle.ROUND) { cornerRadius = 30; image = makeRoundedCorner(image, cornerRadius); } int borderSize = size; int w = image.getWidth() + borderSize; int h = image.getHeight() + borderSize; BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = output.createGraphics(); g2.setComposite(AlphaComposite.Src); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(color == null ? Color.WHITE : color); g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius)); // ... then compositing the image on top, // using the white shape from above as alpha source g2.setComposite(AlphaComposite.SrcAtop); g2.drawImage(image, size, size, null); g2.dispose(); return output; } /** * 生成圆角图片 * * @param image 原始图片 * @param cornerRadius 圆角的弧度 * @return 返回圆角图 */ public static BufferedImage makeRoundedCorner(BufferedImage image, int cornerRadius) { int w = image.getWidth(); int h = image.getHeight(); BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = output.createGraphics(); // This is what we want, but it only does hard-clipping, i.e. aliasing // g2.setClip(new RoundRectangle2D ...) // so instead fake soft-clipping by first drawing the desired clip shape // in fully opaque white with antialiasing enabled... g2.setComposite(AlphaComposite.Src); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(Color.WHITE); g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius)); // ... then compositing the image on top, // using the white shape from above as alpha source g2.setComposite(AlphaComposite.SrcAtop); g2.drawImage(image, -5, -5, null); g2.dispose(); return output; } }

除生成类,其它都是工具类。

就到这儿吧!

还差一个logoStyle的eNum

public enum LogoStyle {
    ROUND, NORMAL;
}

你可能感兴趣的:(java)