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
添加去除白邊功能。
