首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 整合 ZXing 生成 QR Code 完整實作:Controller、工具類、表單驗證三層架構

     編輯:Java 相關知識

ZXing(Zebra Crossing)是 Java 生態系最成熟的條碼/QR Code 處理庫。本文提供生產級三層架構實作:表單驗證層(參數綁定與校驗)、工具核心層(編碼、去白邊、圖片輸出)、控制器層(HTTP 回應處理、資源釋放),並解析關鍵技術細節與擴展建議。

依賴引入(Maven)


<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>

core 為純 Java 邏輯,javase 提供 BufferedImage 整合與 MatrixToImageWriter 便利類。

第一層:請求表單(QRCodeRequestForm)


public class QRCodeRequestForm extends BaseForm {
    private int width = 100;
    private int height = 100;
    private String imageType = "png";
    
    @NotBlank // 或自訂 @VNotEmpty
    private String content;

    // Getter/Setter 省略...
}

設計要點:

  • 預設值降低客戶端複雜度(100x100 px、PNG)。
  • @NotBlank 確保內容非空,避免生成空碼。
  • 繼承 BaseForm 統一驗證錯誤處理(如統一回傳 JSON 錯誤碼)。

第二層:核心工具類(QRCodeUtil)


@Component // 註冊為 Spring Bean,支援注入
public class QRCodeUtil {
    private static final Logger log = LoggerFactory.getLogger(QRCodeUtil.class);
    
    public static void encode(String contents, int width, int height, String format, OutputStream output) {
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.MARGIN, 1); // 邊距最小化
        
        try {
            BitMatrix bitMatrix = new MultiFormatWriter()
                .encode(contents, BarcodeFormat.QR_CODE, width, height, hints);
            
            // 1. 去白邊:計算實際內容邊界
            int[] rect = bitMatrix.getEnclosingRectangle();
            if (rect != null) {
                int resWidth = rect[2] + 1;
                int resHeight = rect[3] + 1;
                BitMatrix trimmed = new BitMatrix(resWidth, resHeight);
                for (int x = 0; x < resWidth; x++) {
                    for (int y = 0; y < resHeight; y++) {
                        if (bitMatrix.get(x + rect[0], y + rect[1])) {
                            trimmed.set(x, y);
                        }
                    }
                }
                bitMatrix = trimmed;
            }
            
            // 2. 輸出圖片
            writeToStream(bitMatrix, format, output);
            
        } catch (Exception e) {
            log.error("QRCode 生成失敗: {}", contents, e);
            throw new RuntimeException("QRCode 生成異常", e);
        }
    }
    
    private static BufferedImage toBufferedImage(BitMatrix matrix) {
        int w = matrix.getWidth(), h = matrix.getHeight();
        BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                img.setRGB(x, y, matrix.get(x, y) ? 0x000000 : 0xFFFFFF);
            }
        }
        return img;
    }
    
    private static void writeToStream(BitMatrix matrix, String format, OutputStream os) throws IOException {
        BufferedImage img = toBufferedImage(matrix);
        if (!ImageIO.write(img, format, os)) {
            throw new IOException("不支援的圖片格式: " + format);
        }
    }
}

關鍵技術解析

  1. 去白邊算法:getEnclosingRectangle() 回傳 [left, top, width, height],僅複製有內容區域,顯著縮小圖片體積、提升掃描辨識率。
  2. 字元編碼:強制 CHARACTER_SET = UTF-8,支援中文、Emoji 等多語言內容。
  3. 錯誤修正等級:可透過 EncodeHintType.ERROR_CORRECTION 設定 L/M/Q/H 四級,預設 L(7% 修正),行銷場景建議 M(15%)。
  4. 執行緒安全:方法為無狀態靜態方法,可並發呼叫。

第三層:控制器(QRCodeController)


@RestController
@RequestMapping("/api/qrcode")
public class QRCodeController {
    private static final Logger log = LoggerFactory.getLogger(QRCodeController.class);
    
    @GetMapping("/generate")
    public void generate(HttpServletResponse response, @Valid QRCodeRequestForm form) {
        // 禁用快取,確保即時生成
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/" + form.getImageType());
        
        try (OutputStream os = response.getOutputStream()) {
            QRCodeUtil.encode(form.getContent(), form.getWidth(), form.getHeight(), form.getImageType(), os);
            os.flush();
        } catch (IOException e) {
            log.error("QRCode 輸出異常", e);
            // 此處無法再寫入 JSON 錯誤,因回應已提交;建議前端處理圖片載入失敗
        }
    }
}

資源管理亮點

  • 使用 try-with-resources 自動關閉 OutputStream,避免洩漏。
  • 設定 Content-Type: image/png 等正確 MIME,瀏覽器可直接 <img src="/api/qrcode/generate?content=..."> 嵌入。
  • 禁用快取頭,確保動態內容(如帶參數的 QR Code)不被瀏覽器/代理快取。

進階擴展建議

  1. Logo 嵌入:生成 QR Code 後,使用 Graphics2D 繪製中央 Logo,需調高錯誤修正等級至 H(30%)。
  2. 非同步生成:大量請求時,採用 CompletableFuture 或訊息佇列異步生成,回傳任務 ID 輪詢結果。
  3. Base64 回傳:前端需內嵌 Data URI 時,Controller 回傳 data:image/png;base64,... 字串。
  4. 批次生成:支援陣列內容,打包 ZIP 下載。
  5. 監控指標:Micrometer 記錄生成耗時、成功率、輸出大小分布。

常見坑點排查

現象原因解決
中文內容掃描亂碼未設定 CHARACTER_SET=UTF-8hints.put(EncodeHintType.CHARACTER_SET, "UTF-8")
圖片四周留白過大預設 MARGIN=4hints.put(EncodeHintType.MARGIN, 1) 或去白邊邏輯
OutputStream 已關閉異常重複 flush/close 或回應已提交try-with-resources 統一管理
生成圖片模糊寬高過小或壓縮失真最小建議 200x200,format=png 無損
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved