一、概述
本教程将详细讲解如何在 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二维码生成与读取的资料请关注代码网其它相关文章!
发表评论