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的方法,根本无效,因为源码中并没有通过这个参数来设置白边的宽度;
大致过程:先根据内容生成二维码,然后根据指定的宽高,对原来的二维码进行放大或缩小,白色边框的宽度=要求的宽高-放大或缩小后的二维码的宽高。
com\google\zxing\qrcode\QRCodeWriter.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("\r\n", ""); } 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
添加去除白边功能。
本文来自博客园,作者:Marydon,转载请注明原文链接:https://www.cnblogs.com/Marydon20170307/p/9546411.html