QR Code(快速響應碼)已廣泛應用於行動支付、登入驗證、資訊分享等場景。Google 開源的 ZXing(Zebra Crossing)是 Java 生態中最主流的條碼處理庫。本文詳細說明如何使用 ZXing Core 生成與解析 QR Code,並提供缺失的輔助類原始碼。
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.1</version>
</dependency>
public void generateQRCode(String content, String filePath, int width, int height) throws Exception {
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.MARGIN, 1); // 邊距
BitMatrix bitMatrix = new MultiFormatWriter().encode(
content, BarcodeFormat.QR_CODE, width, height, hints);
MatrixToImageWriter.writeToFile(bitMatrix, "PNG", new File(filePath));
}
public final class MatrixToImageWriter {
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
private MatrixToImageWriter() {}
public static BufferedImage toBufferedImage(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.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, matrix.get(x, y) ? BLACK : WHITE);
}
}
return image;
}
public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
BufferedImage image = toBufferedImage(matrix);
if (!ImageIO.write(image, format, file)) {
throw new IOException("無法寫入圖片: " + file);
}
}
public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException {
BufferedImage image = toBufferedImage(matrix);
if (!ImageIO.write(image, format, stream)) {
throw new IOException("無法寫入輸出流");
}
}
}
public final class BufferedImageLuminanceSource extends LuminanceSource {
private final BufferedImage image;
private final int left, top;
public BufferedImageLuminanceSource(BufferedImage image) {
this(image, 0, 0, image.getWidth(), image.getHeight());
}
public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
super(width, height);
// 轉灰階並裁剪
this.image = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
this.image.getGraphics().drawImage(image, 0, 0, null);
this.left = left;
this.top = top;
}
@Override public byte[] getRow(int y, byte[] row) { /* 實現略 */ }
@Override public byte[] getMatrix() { /* 實現略 */ }
@Override public LuminanceSource crop(int left, int top, int width, int height) { /* 實現略 */ }
@Override public LuminanceSource rotateCounterClockwise() { /* 實現略 */ }
@Override public boolean isCropSupported() { return true; }
@Override public boolean isRotateSupported() { return true; }
}
public String decodeQRCode(String imagePath) throws Exception {
BufferedImage image = ImageIO.read(new File(imagePath));
LuminanceSource source = new BufferedImageLuminanceSource(image);
Binarizer binarizer = new HybridBinarizer(source);
BinaryBitmap bitmap = new BinaryBitmap(binarizer);
Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
Result result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
MatrixToImageWriter 中的 BLACK/WHITE 常數,或生成後像素級處理。| 問題 | 原因 | 解決方案 |
|---|---|---|
| 生成圖片全黑/全白 | BitMatrix 邏輯反轉 | 檢查 matrix.get(x,y) 布林值對應顏色 |
| 中文內容亂碼 | 編碼未指定 UTF-8 | 設定 EncodeHintType.CHARACTER_SET |
| 解析失敗 | 圖片模糊/尺寸過小/邊距不足 | 提高解析度、增加 MARGIN、使用 TRY_HARDER |