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

Java 生成二維碼完整教程:基於 ZXing 3.x 實現帶 Logo 二維碼生成與解析

     編輯:Java 相關知識

技術選型

採用 Google ZXing (Zebra Crossing) 3.x 核心庫,純 Java 實現,無原生依賴,支持 QR Code、Data Matrix、Aztec 等多種格式。

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>

核心工具類

public final class QRCodeUtil {
    private static final String CHARSET = "UTF-8";
    private static final String FORMAT = "PNG";
    private static final int QR_SIZE = 300;     // 二維碼邊長
    private static final int LOGO_SIZE = 60;    // Logo 最大邊長

    private QRCodeUtil() {}

    /** 生成帶 Logo 二維碼到文件 */
    public static void encode(String content, String logoPath, String destDir, boolean compressLogo)
            throws Exception {
        BufferedImage image = createImage(content, logoPath, compressLogo);
        mkdirs(destDir);
        String fileName = System.currentTimeMillis() + ".png";
        ImageIO.write(image, FORMAT, new File(destDir, fileName));
    }

    /** 生成無 Logo 二維碼 */
    public static void encode(String content, String destDir) throws Exception {
        encode(content, null, destDir, false);
    }

    /** 生成到輸出流 */
    public static void encode(String content, String logoPath, OutputStream out, boolean compressLogo)
            throws Exception {
        ImageIO.write(createImage(content, logoPath, compressLogo), FORMAT, out);
    }

    /** 核心繪製邏輯 */
    private static BufferedImage createImage(String content, String logoPath, boolean compressLogo)
            throws Exception {
        // 1. 編碼配置
        Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 30% 容錯
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        hints.put(EncodeHintType.MARGIN, 1); // 邊距 1 模塊

        // 2. 生成位矩陣
        BitMatrix matrix = new MultiFormatWriter()
                .encode(content, BarcodeFormat.QR_CODE, QR_SIZE, QR_SIZE, hints);

        // 3. 繪製基礎二維碼
        BufferedImage image = new BufferedImage(QR_SIZE, QR_SIZE, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < QR_SIZE; x++) {
            for (int y = 0; y < QR_SIZE; y++) {
                image.setRGB(x, y, matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }

        // 4. 嵌入 Logo
        if (StringUtils.isNotBlank(logoPath)) {
            insertLogo(image, logoPath, compressLogo);
        }
        return image;
    }

    private static void insertLogo(BufferedImage qr, String logoPath, boolean compress) throws IOException {
        File file = new File(logoPath);
        if (!file.exists()) {
            log.warn("Logo 文件不存在: {}", logoPath);
            return;
        }
        Image logo = ImageIO.read(file);
        int w = logo.getWidth(null), h = logo.getHeight(null);
        if (compress) {
            if (w > LOGO_SIZE) w = LOGO_SIZE;
            if (h > LOGO_SIZE) h = LOGO_SIZE;
            logo = logo.getScaledInstance(w, h, Image.SCALE_SMOOTH);
        }
        // 繪製到中心
        Graphics2D g = qr.createGraphics();
        int x = (QR_SIZE - w) / 2;
        int y = (QR_SIZE - h) / 2;
        g.drawImage(logo, x, y, w, h, null);
        // 可選:白底圓角邊框
        g.setStroke(new BasicStroke(3f));
        g.draw(new RoundRectangle2D.Float(x, y, w, h, 6, 6));
        g.dispose();
    }

    /** 解析二維碼 */
    public static String decode(File file) throws Exception {
        BufferedImage img = ImageIO.read(file);
        if (img == null) return null;
        LuminanceSource source = new BufferedImageLuminanceSource(img);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
        hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
        Result result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

    public static String decode(String path) throws Exception {
        return decode(new File(path));
    }

    private static void mkdirs(String dir) {
        File f = new File(dir);
        if (!f.exists()) f.mkdirs();
    }
}

BufferedImageLuminanceSource 適配類

ZXing 3.x 需要實現 LuminanceSource 接口讀取圖片像素,完整代碼見原文,核心是將 BufferedImage 轉灰度像素數組。

使用示例

public static void main(String[] args) throws Exception {
    String url = "https://www.example.com";

    // 1. 純二維碼
    QRCodeUtil.encode(url, "d:/qrcode/plain");

    // 2. 帶 Logo 二維碼
    QRCodeUtil.encode(url, "d:/logo.png", "d:/qrcode/with_logo", true);

    // 3. 解析驗證
    File testFile = new File("d:/qrcode/with_logo/最新生成的文件.png");
    System.out.println("解析結果: " + QRCodeUtil.decode(testFile));
}

關鍵參數調優指南

參數推薦值影響
ErrorCorrectionLevelH (30%)容錯率高,遮蓋 Logo 後仍可掃描;代價是存儲內容略減
MARGIN1~2邊距過大浪費空間,過小可能識別困難
QR_SIZE300~500打印場景建議 ≥ 400px,純屏幕展示 300 足夠
LOGO_SIZEQR_SIZE / 5超過 1/3 會導致掃描失敗,圓角邊框可提升識別率

常見問題排查

  • 掃不出:降低 Logo 尺寸、提高容錯級別、增加邊距、確保對比度(深色碼淺色底)。
  • 中文亂碼:編碼/解碼統一指定 CHARSET = UTF-8。
  • 內存溢出:批量生成大圖時及時 image.flush()、分批處理、避免同時持有大量 BufferedImage。

進階:Spring Boot 整合 REST 接口

@RestController
@RequestMapping("/api/qrcode")
public class QRCodeController {
    @GetMapping("/generate")
    public void generate(@RequestParam String content,
                         @RequestParam(required = false) String logo,
                         HttpServletResponse resp) throws Exception {
        resp.setContentType("image/png");
        QRCodeUtil.encode(content, logo, resp.getOutputStream(), true);
    }

    @PostMapping("/decode")
    public String decode(@RequestParam("file") MultipartFile file) throws Exception {
        return QRCodeUtil.decode(file.getInputStream()); // 需擴展支持 InputStream
    }
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved