在很多的场景下我们需要用到二维码,这里就通过google的zxing来对二维码进行实现。
二维码生成:
1.导入依赖:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>2.2</version>
</dependency>
2.编写二维码工具类,用于讲生成的二维码图片通过流的形式写到浏览器,同时支持在二维码中间添加定制图片:
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import static org.apache.catalina.manager.Constants.CHARSET;
public class RecodeUtil {
private static final int WIDTH = 30;
private static final int HEIGHT = 30;
public static void creatRrCode(String contents, int width, int height, HttpServletResponse response) throws Exception {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); //容错级别最高
hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); //设置字符编码
hints.put(EncodeHintType.MARGIN, 1); //二维码空白区域,最小为0也有白边,只是很小,最小是6像素左右
try {
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints); // 1、读取文件转换为字节数组
BufferedImage image = toBufferedImage(bitMatrix);
//转换成png格式的IO流
ImageIO.write(image, "png", response.getOutputStream());
} catch (WriterException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* image流数据处理
*/
public static BufferedImage toBufferedImage(BitMatrix matrix) throws Exception {
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++) {//0xFF000000 0xFFFFFFFF
//https://blog.csdn.net/cgwcgw_/article/details/21155229 颜色查询
image.setRGB(x, y, matrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
insertImage(image,"D:/head.jpg",true,width,height);//调用insertImage函数插入图片
return image;
}
/**
* 插入内嵌图片
* @param source
* @param imgPath 要插入图片路径
* @param needCompress 要插入图片的像素是否大于60
* @throws Exception
*/
private static void insertImage(BufferedImage source, String imgPath,
boolean needCompress,int qrWidth,int qrHeight) throws Exception {
File file = new File(imgPath);
if(!file.exists()){
System.err.print(""+imgPath+"路径不存在!");
return;
}
Image src = ImageIO.read(new File(imgPath));
int width = src.getWidth(null);//获得原宽度
int height = src.getHeight(null);//获得源高度
if(needCompress){//比较要插入的图片的宽度是否大于设定的WIDTH=30像素宽
if(width>WIDTH){
width = WIDTH;
}
if(height>HEIGHT){//比较要插入的图片的高度是否大于设定的HEIGTH=30像素宽
height = HEIGHT;
}
Image image = src.getScaledInstance(width, height, //把image对象的getScaledInstance方法把图片缩小heightXwidth像素大小
Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width,height,///创建一个透明色的BufferedImage对象
BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();//获得画笔
g.drawImage(image, 0, 0, null);//绘制指定图像中当前可用的image图像,图像的左上角位于该图形上下文坐标(0,0)的 (x, y)
}
//开始画内嵌图片
Graphics2D graph = source.createGraphics();
//计算绘画坐标
int x = (qrWidth-width)/2;
int y = (qrHeight-height)/2;
graph.drawImage(src, x, y, width, height, null);//内嵌坐标为(x,y)的地方
Shape shape = new RoundRectangle2D.Float(x,y,width,width,6,6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
public static String decode(File file) throws Exception {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
Hashtable hints = new Hashtable();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
result = new MultiFormatReader().decode(bitmap, hints);
String resultStr = result.getText();
return resultStr;
}
}
3.提供请求控制器:
@RestController
public class QRCodeController {
@GetMapping("/qrcode")
public void qrcode(HttpServletRequest request, HttpServletResponse response) throws Exception {
String content = "你是猪";
long l = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
content += String.valueOf(l);
if (StringUtils.isBlank(content)) {
System.out.println("404");
return;
}
//调用工具类,生成二维码
RecodeUtil.creatRrCode(content, 180, 180, response); //180为图片高度和宽度
}
@PostMapping("/qrcode/parse")
public void read(MultipartFile file) throws Exception {
File toFile = null;
InputStream ins = null;
ins = file.getInputStream();
toFile = new File(file.getOriginalFilename());
inputStreamToFile(ins, toFile);
ins.close();
RecodeUtil.decode(toFile);
}
//获取流文件
private static void inputStreamToFile(InputStream ins, File file) {
try {
OutputStream os = new FileOutputStream(file);
int bytesRead = 0;
byte[] buffer = new byte[8192];
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.通过以上的代码就完成了后端代码的编写,接下去看一下前端代码。我前端代码基于 Vue 。:
// ......省略代码
<button v-on:click="qrcode">二维码</button><br/>
<img :src = "qrcodeImage" >
<img src = "http://localhost:8889/qrcode" >
export default {
name: 'HelloWorld',
data () {
return {
qrcodeImage: ''
}
},
methods: {
qrcode: function () {
this.qrcodeImage = null
this.$axios({
method: 'get',
url: '/api/qrcode',
responseType: 'arraybuffer'
}).then(function (res) {
return 'data:image/png;base64,' + btoa(
new Uint8Array(res.data)
.reduce((data, byte) => data + String.fromCharCode(byte), '')
)
}).then(data => {
this.qrcodeImage = data
}).catch(function (err) {
alert(err)
})
}
}
5.启动项目,不点击二维码按钮的时候只有一个固定不变的二维码。当点击二维码按钮可以看到一下效果:
这样子就可以实现我们的二维码。
二维码解析:
二维码解析可以通过调用上述 /qrcode/parse 接口,这里采用 postman作为演示,我们先将上面得到的二维码截图保存:
然后就可以得到二维码的内容了。解析的代码上面也已经给出。