SpringBoot集成ZXing实现二维码的生成与读取功能
更新时间:2026年03月21日 14:35:57 作者:Amour恋空
本教程将详细讲解如何在 Spring Boot 项目中集成 ZXing 库实现二维码的生成(返回 Base64 编码)和读取(解析图片的二维码)功能,并覆盖常见异常处理、参数优化等实战要点,适合 Java 开发新手快速上手,需要的朋友可以参考下
一、概述
本教程将详细讲解如何在 Spring Boot 项目中集成 ZXing 库实现二维码的生成(返回 Base64 编码)和读取(解析图片的二维码)功能,并覆盖常见异常处理、参数优化等实战要点,适合 Java 开发新手快速上手。
二、环境准备
2.1 技术栈
- 框架:Spring Boot 2.x/3.x(本人测试SpringBoot版本影响较小)
- 核心依赖:ZXing(二维码处理)
- 开发工具:IDEA/Eclipse、Maven
2.2 依赖引入
在 pom.xml 中添加 ZXing 核心依赖:
<!-- SpringBoot启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot的web启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ZXing 二维码核心依赖 -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version> <!-- 推荐使用最新稳定版 -->
</dependency>
<!-- ZXing JavaSE 扩展(处理图片) -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.5.2</version>
</dependency>三、工具类封装
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
//注意JDK11以上javax包名需要修改为jakarta
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* 二维码工具类(生成+读取)
* 包含异常处理、参数优化、Base64 转换等功能
*/
public class QRCodeUtil {
// 默认二维码宽度/高度
private static final int DEFAULT_SIZE = 300;
// 默认字符编码
private static final String DEFAULT_CHARSET = "UTF-8";
// 二维码颜色(黑色)
private static final int QR_CODE_COLOR = 0xFF000000;
// 二维码背景色(白色)
private static final int QR_CODE_BACKGROUND = 0xFFFFFFFF;
/**
* 生成二维码 BufferedImage 对象
* @param content 二维码内容(必填)
* @return BufferedImage 二维码图片
* @throws WriterException 生成失败异常
*/
public static BufferedImage createQRCode(String content) throws WriterException {
return createQRCode(content, DEFAULT_SIZE, DEFAULT_SIZE);
}
/**
* 自定义尺寸生成二维码
* @param content 二维码内容
* @param width 宽度
* @param height 高度
* @return BufferedImage
* @throws WriterException 内容为空/尺寸非法时抛出
*/
public static BufferedImage createQRCode(String content, int width, int height) throws WriterException {
// 前置校验
if (content == null || content.isEmpty()) {
throw new WriterException("二维码内容不能为空");
}
if (width <= 0 || height <= 0) {
throw new WriterException("二维码尺寸必须大于0");
}
// 配置二维码参数(关键:解决中文乱码、提升容错率)
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, DEFAULT_CHARSET); // 字符编码
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 最高容错率(30%)
hints.put(EncodeHintType.MARGIN, 1); // 边距(0=无白边)
// 生成二维码矩阵
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
// 转换为 BufferedImage(自定义颜色)
MatrixToImageConfig config = new MatrixToImageConfig(QR_CODE_COLOR, QR_CODE_BACKGROUND);
return MatrixToImageWriter.toBufferedImage(bitMatrix, config);
}
/**
* 将 BufferedImage 转换为字节数组
* @param image 二维码图片
* @return 字节数组
* @throws IOException 图片转换失败
*/
public static byte[] imageToBytes(BufferedImage image) throws IOException {
if (image == null) {
throw new IOException("图片对象不能为空");
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", outputStream);
return outputStream.toByteArray();
}
/**
* 将字节数组转换为 Base64 编码字符串(纯编码,无前缀)
* @param bytes 图片字节数组
* @return Base64 字符串
*/
public static String imageToBase64(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException("字节数组不能为空");
}
return Base64.getEncoder().encodeToString(bytes);
}
/**
* 读取图片流中的二维码内容
* @param inputStream 图片输入流(如文件流、网络流)
* @return 二维码内容
* @throws NotFoundException 未识别到二维码
* @throws IOException 图片读取失败
* @throws ChecksumException 二维码数据校验失败
* @throws FormatException 二维码格式错误
*/
public static String readQRCode(InputStream inputStream) throws NotFoundException, IOException, ChecksumException, FormatException {
if (inputStream == null) {
throw new IOException("图片输入流不能为空");
}
BufferedImage image = ImageIO.read(inputStream);
if (image == null) {
throw new IOException("无法解析图片,请检查文件格式");
}
// 配置解析参数(提升识别率)
Map<DecodeHintType, Object> hints = new HashMap<>();
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); // 尝试更高精度解析
hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE); // 仅解析二维码
hints.put(DecodeHintType.CHARACTER_SET, DEFAULT_CHARSET); // 字符编码
// 解析二维码
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image)));
QRCodeReader qrCodeReader = new QRCodeReader();
Result result = qrCodeReader.decode(binaryBitmap, hints);
return result.getText();
}
/**
* 读取 Base64 编码中的二维码内容
* @param base64Str Base64 字符串(支持带/不带 data:image/png;base64, 前缀)
* @return 二维码内容
* @throws Exception 解析失败
*/
public static String readQRCodeFromBase64(String base64Str) throws Exception {
if (base64Str == null || base64Str.isEmpty()) {
throw new IllegalArgumentException("Base64 字符串不能为空");
}
// 移除 Base64 前缀(如果有)
String pureBase64 = base64Str.replace("data:image/png;base64,", "");
// 解码为字节数组并转换为输入流
byte[] bytes = Base64.getDecoder().decode(pureBase64);
try (InputStream inputStream = new java.io.ByteArrayInputStream(bytes)) {
return readQRCode(inputStream);
}
}
}
四、接口实现(生成 + 读取)
4.1 二维码生成接口(支持显示图片/Base64)
import com.google.zxing.NotFoundException;
import com.google.zxing.WriterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
//注意JDK11以上javax包名需要修改为jakarta
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
/**
* 二维码生成接口
*/
@RestController
@RequestMapping("/qrcode")
public class QRCodeGenerateController {
/**
* 生成二维码(直接在浏览器显示图片)
* @param content 二维码内容(必填)
* @param width 宽度(默认300)
* @param height 高度(默认300)
*/
@GetMapping("/img")
public void generate( @RequestParam(required = true) String content,
@RequestParam(defaultValue = "300") int width,
@RequestParam(defaultValue = "300") int height, HttpServletResponse response) throws Exception {
try {
response.setContentType("image/png");
BufferedImage image = QRCodeUtil.createQRCode(content, width, height);
com.example.demo.d1.QRCodeUtil.imageToBytes(image);
ImageIO.write(image, "png", response.getOutputStream());
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 生成二维码并返回 Base64 编码(前端可直接用于 img 标签)
* @param content 二维码内容(必填)
* @param width 宽度(默认300)
* @param height 高度(默认300)
* @return 结构化响应
*/
@GetMapping("/generate")
public Map<String, Object> generateQRCode(
@RequestParam(required = true) String content,
@RequestParam(defaultValue = "300") int width,
@RequestParam(defaultValue = "300") int height) {
Map<String, Object> result = new HashMap<>();
try {
// 生成二维码图片
BufferedImage qrImage = QRCodeUtil.createQRCode(content, width, height);
// 转换为字节数组
byte[] qrBytes = QRCodeUtil.imageToBytes(qrImage);
// 转换为 Base64(带前端可直接使用的前缀)
String base64 = "data:image/png;base64," + QRCodeUtil.imageToBase64(qrBytes);
// 响应结果
result.put("code", 200);
result.put("msg", "二维码生成成功");
result.put("data", base64);
} catch (WriterException e) {
// 处理生成失败异常(内容为空/尺寸非法等)
result.put("code", 400);
result.put("msg", "二维码生成失败:" + e.getMessage());
result.put("data", null);
} catch (Exception e) {
// 处理其他未知异常
result.put("code", 500);
result.put("msg", "服务器异常:" + e.getMessage());
result.put("data", null);
}
return result;
}
}
4.2 二维码读取接口(支持文件 / Base64)
import com.google.zxing.NotFoundException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.HashMap;
import java.util.Map;
/**
* 二维码读取接口
*/
@RestController
@RequestMapping("/qrcode")
public class QRCodeReadController {
/**
* 解析上传的图片文件中的二维码
* @param file 图片文件(支持 png/jpg/jpeg)
* @return 解析结果
*/
@PostMapping("/file")
public Map<String, Object> readQRCodeFromFile(@RequestParam("file") MultipartFile file) {
Map<String, Object> result = new HashMap<>();
try {
// 前置校验
if (file.isEmpty()) {
result.put("code", 400);
result.put("msg", "上传的文件不能为空");
return result;
}
String contentType = file.getContentType();
if (contentType == null || !contentType.startsWith("image/")) {
result.put("code", 400);
result.put("msg", "请上传图片文件(png/jpg/jpeg)");
return result;
}
// 解析二维码
String content = QRCodeUtil.readQRCode(file.getInputStream());
result.put("code", 200);
result.put("msg", "二维码解析成功");
result.put("data", content);
} catch (NotFoundException e) {
// 核心异常:未识别到二维码
result.put("code", 400);
result.put("msg", "未识别到二维码:图片中无有效二维码或二维码模糊/破损");
result.put("data", null);
} catch (Exception e) {
result.put("code", 500);
result.put("msg", "解析失败:" + e.getMessage());
result.put("data", null);
}
return result;
}
/**
* 解析 Base64 编码中的二维码
* @param base64Str Base64 字符串(支持带/不带前缀)
* @return 解析结果
*/
@PostMapping("/base64")
public Map<String, Object> readQRCodeFromBase64(@RequestParam("base64") String base64Str) {
Map<String, Object> result = new HashMap<>();
try {
String content = QRCodeUtil.readQRCodeFromBase64(base64Str);
result.put("code", 200);
result.put("msg", "解析成功");
result.put("data", content);
} catch (NotFoundException e) {
result.put("code", 400);
result.put("msg", "未识别到二维码");
} catch (IllegalArgumentException e) {
result.put("code", 400);
result.put("msg", "Base64 格式错误:" + e.getMessage());
} catch (Exception e) {
result.put("code", 500);
result.put("msg", "解析失败:" + e.getMessage());
}
return result;
}
}
五、常见异常与解决方案
5.1 核心异常列表
| 异常类型 | 异常说明 | 解决方案 |
|---|---|---|
| com.google.zxing.NotFoundException | 未识别到二维码 | 1. 检查图片是否包含有效二维码 2. 确保二维码清晰、无遮挡、分辨率≥200px 3. 解析时启用 TRY_HARDER 参数4. 避免二维码角度倾斜过大 |
| com.google.zxing.WriterException | 二维码生成失败 | 1. 检查内容是否为空 2. 确保尺寸参数大于 0 3. 内容过长时缩短(或提升容错级别) |
| java.io.IOException | 图片读取 / 转换失败 | 1. 检查文件格式是否为 png/jpg 2. 确保输入流未提前关闭 3. 验证 Base64 编码是否完整 |
| IllegalArgumentException | 参数非法 | 1. 校验 Base64 字符串是否为空 2. 检查文件是否为空 3. 验证尺寸 / 编码参数合法性 |
5.2 通用优化建议
- 提升生成容错率:使用 ErrorCorrectionLevel.H(最高级别),即使二维码被遮挡 30% 仍可识别;
- 解决中文乱码:生成 / 解析时统一设置 CHARACTER_SET 为 UTF-8;
- 优化解析成功率:
- 解析时启用 TRY_HARDER 参数;
- 对图片进行预处理(灰度化、缩放至合适尺寸);
- 使用 MultiFormatReader 替代 QRCodeReader(支持多码制解析)
- Base64 兼容性:返回时拼接 data:image/png;base64, 前缀,前端可直接用于 ;
- 参数校验:所有接口必须校验入参(内容、文件、尺寸),避免空指针 / 非法参数异常。
六、测试
可以直接使用如下测试页面进行测试,如修改请求url请根据修改内容同步修改页面测试地址
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>二维码识别&生成工具</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
padding: 30px;
font-size: 16px;
}
.container {
max-width: 500px;
margin: 0 auto;
}
h2 {
margin-bottom: 20px;
text-align: center;
}
/* 新增:生成和识别模块的分隔 */
.module {
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.module:last-child {
border-bottom: none;
}
.upload-box {
border: 2px dashed #ccc;
padding: 40px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
}
.upload-box:hover {
border-color: #409eff;
}
#preview {
max-width: 100%;
max-height: 300px;
margin: 20px 0;
display: none;
}
/* 新增:生成二维码的预览样式 */
#qrcode-preview {
max-width: 200px;
max-height: 200px;
margin: 20px auto;
display: none;
}
#result {
margin-top: 20px;
padding: 15px;
background: #f5f5f5;
border-radius: 6px;
white-space: pre-wrap;
}
button {
padding: 10px 20px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background: #ccc;
}
/* 新增:生成二维码的输入框样式 */
.generate-input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 15px 0;
resize: vertical;
min-height: 80px;
}
</style>
</head>
<body>
<div class="container">
<!-- 新增:二维码生成模块 -->
<div class="module">
<h2>二维码生成</h2>
<textarea class="generate-input" id="qrcode-text" placeholder="请输入要生成二维码的文本内容(如网址、文字、手机号等)"></textarea>
<div style="text-align: center; margin: 10px 0;">
<button id="generateBtn">生成二维码</button>
</div>
<!-- 生成的二维码预览 -->
<div style="text-align: center;">
<img id="qrcode-preview" alt="生成的二维码">
</div>
</div>
<!-- 原有:二维码识别模块 -->
<div class="module">
<h2>二维码图片识别</h2>
<div class="upload-box" onclick="document.getElementById('file').click()">
点击或拖拽上传二维码图片
</div>
<input type="file" id="file" accept="image/*" style="display: none;">
<img id="preview" alt="预览图">
<div style="text-align: center; margin: 10px 0;">
<button id="recognizeBtn" disabled>开始识别</button>
</div>
<div id="result"></div>
</div>
</div>
<script>
// ========== 原有:二维码识别功能 ==========
const fileInput = document.getElementById('file');
const preview = document.getElementById('preview');
const recognizeBtn = document.getElementById('recognizeBtn');
const result = document.getElementById('result');
// 选择图片
fileInput.onchange = function (e) {
const file = e.target.files[0];
if (!file) return;
// 预览
const url = URL.createObjectURL(file);
preview.src = url;
preview.style.display = 'block';
recognizeBtn.disabled = false;
// 识别
recognizeBtn.onclick = async function () {
result.innerText = "识别中...";
recognizeBtn.disabled = true;
const formData = new FormData();
formData.append("file", file);
try {
const res = await fetch("http://localhost:8080/qrcode/read", {
method: "POST",
body: formData
});
const jsonData = await res.json();
result.innerText = "识别结果:\n" + jsonData.data;
} catch (err) {
result.innerText = "识别失败:" + err.message;
} finally {
recognizeBtn.disabled = false;
}
};
}
// ==========二维码生成功能 ==========
const generateBtn = document.getElementById('generateBtn');
const qrcodeText = document.getElementById('qrcode-text');
const qrcodePreview = document.getElementById('qrcode-preview');
// 生成二维码按钮点击事件
generateBtn.onclick = async function () {
const text = qrcodeText.value.trim();
if (!text) {
alert("请输入要生成二维码的内容!");
return;
}
generateBtn.disabled = true;
generateBtn.innerText = "生成中...";
qrcodePreview.style.display = 'none';
try {
// 调用后端生成二维码接口(需后端配合实现/qrcode/generate接口)
const res = await fetch("http://localhost:8080/qrcode/generate?content="+text, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
if (!res.ok) throw new Error("生成失败");
const jsonData = await res.json();
// 将后端返回的二维码图片转为URL显示
// const blob = await res.blob();
// const qrUrl = URL.createObjectURL(blob);
qrcodePreview.src = jsonData.data;
qrcodePreview.style.display = 'block';
} catch (err) {
alert("二维码生成失败:" + err.message);
} finally {
generateBtn.disabled = false;
generateBtn.innerText = "生成二维码";
}
}
</script>
</body>
</html>以上就是SpringBoot集成ZXing实现二维码的生成与读取功能的详细内容,更多关于SpringBoot ZXing二维码生成与读取的资料请关注脚本之家其它相关文章!
相关文章
Java中使用Spring Retry实现重试机制的流程步骤
这篇文章主要介绍了我们将探讨如何在Java中使用Spring Retry来实现重试机制,重试机制在处理临时性故障和提高系统稳定性方面非常有用,文中通过代码示例介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下2024-07-07


最新评论