package com.jfinal.render;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;
import java.util.HashMap;
import java.util.Map;
/**
* QrCodeRender 生成二维码
*/
public class QrCodeRender extends Render {
private String content;
private int width;
private int height;
private ErrorCorrectionLevel errorCorrectionLevel;
/**
* 构造方法,经测试不指定纠错参数时,默认使用的是 'L' 最低级别纠错参数
* @param content 二维码携带内容
* @param width 二维码宽度
* @param height 二维码高度
*/
public QrCodeRender(String content, int width, int height) {
init(content, width, height, null);
}
/**
* 带有纠错级别参数的构造方法,生成带有 logo 的二维码采用纠错原理
* 使用 ErrorCorrectionLevel.H 参数提升纠错能力
*
* ErrorCorrectionLevel 是枚举类型,纠错能力从高到低共有四个级别:
* H = ~30% correction
* Q = ~25% correction
* M = ~15% correction
* L = ~7%
*
* 使用的时候直接这样:ErrorCorrectionLevel.H
*/
public QrCodeRender(String content, int width, int height, ErrorCorrectionLevel errorCorrectionLevel) {
init(content, width, height, errorCorrectionLevel);
}
/**
* 带有纠错级别参数的构造方法,纠错能力从高到低共有四个级别:'H'、'Q'、'M'、'L'
*/
public QrCodeRender(String content, int width, int height, char errorCorrectionLevel) {
init(content, width, height, errorCorrectionLevel);
}
private void init(String content, int width, int height, char errorCorrectionLevel) {
if (errorCorrectionLevel == 'H') {
init(content, width, height, ErrorCorrectionLevel.H);
} else if (errorCorrectionLevel == 'Q') {
init(content, width, height, ErrorCorrectionLevel.Q);
} else if (errorCorrectionLevel == 'M') {
init(content, width, height, ErrorCorrectionLevel.M);
} else if (errorCorrectionLevel == 'L') {
init(content, width, height, ErrorCorrectionLevel.L);
} else {
throw new IllegalArgumentException("errorCorrectionLevel 纠错级别参数值,从高到低必须为: 'H'、'Q'、'M'、'L'");
}
}
private void init(String content, int width, int height, ErrorCorrectionLevel errorCorrectionLevel) {
if (StrKit.isBlank(content)) {
throw new IllegalArgumentException("content 不能为空");
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("width 与 height 不能小于 0");
}
this.content = content;
this.width = width;
this.height = height;
this.errorCorrectionLevel = errorCorrectionLevel;
}
public void render() {
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/png");
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
hints.put(EncodeHintType.MARGIN, 0); //去掉白色边框,极度重要,否则二维码周围的白边会很宽
if (errorCorrectionLevel != null) {
hints.put(EncodeHintType.ERROR_CORRECTION, errorCorrectionLevel);
}
try {
// MultiFormatWriter 可支持多种格式的条形码,在此直接使用 QRCodeWriter,通过查看源码可知少创建一个对象
// BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
QRCodeWriter writer = new QRCodeWriter();
BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
// 经测试 200 X 200 大小的二维码使用 "png" 格式只有 412B,而 "jpg" 却达到 15KB
MatrixToImageWriter.writeToStream(bitMatrix, "png", response.getOutputStream()); // format: "jpg"、"png"
}catch (Exception e) {
throw new RenderException(e);
}
}
}