之前在做一個小項目的時候,用到了二維碼技術,我同學看到我的項目後就問我怎麼做到的,我一時半會也說不清,於是就想寫一篇文章簡單介紹一下二維碼技術,下次再有同學問到,我就直接給他看我的博客就好了,哈哈。
廢話不多說,咱們開始吧,開始之前還是先給大家看看我在實際應用中的效果,如下圖:
簡單介紹下二維碼:二維碼其實就是一種編碼技術,只是這種編碼技術是用在圖片上了,將給定的一些文字,數字轉換爲一張經過特定編碼的圖片,而解析二維碼則相反,就是將一張經過編碼的圖片解析爲數字或者文字。
當然了,這種編碼的低層實現並不是那麼的簡單,都是那些頂級研發人員做的事,我們只需要簡單瞭解並知道怎麼使用就可以了。
那麼肯定是要導入jar包依賴的吧,那不用說,是要導包的,我用的是谷歌提供的一個com.google.zxing.core版本是3.3.0的。
下面我們直接創建一個maven項目,用maven管理jar包很方便,而且我們需要的jar包基本都可以從官網下載進行依賴的添加。
創建好maven項目後,在項目的pom.xml中添加依賴:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.0</version>
</dependency>
上面這段依賴可以直接到maven的官網進行copy,在maven依賴查詢官網直接搜索zxing,我用的是3.3.0的版本。
相信大家對maven也已經比較熟悉了,具體大家可參考我的文章裏面的《maven入門筆記》
jar包依賴添加完後,我們直接上代碼:
第一個類:
package com.yc.qrcode;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import com.google.zxing.LuminanceSource;
public class BufferedImageLuminanceSource extends LuminanceSource {
private final BufferedImage image;
private final int left;
private final int top;
public BufferedImageLuminanceSource(BufferedImage image) {
this(image, 0, 0, image.getWidth(), image.getHeight());
}
public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
super(width, height);
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
if (left + width > sourceWidth || top + height > sourceHeight) {
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
}
for (int y = top; y < top + height; y++) {
for (int x = left; x < left + width; x++) {
if ((image.getRGB(x, y) & 0xFF000000) == 0) {
image.setRGB(x, y, 0xFFFFFFFF); // = white
}
}
}
this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);
this.image.getGraphics().drawImage(image, 0, 0, null);
this.left = left;
this.top = top;
}
public byte[] getRow(int y, byte[] row) {
if (y < 0 || y >= getHeight()) {
throw new IllegalArgumentException("Requested row is outside the image: " + y);
}
int width = getWidth();
if (row == null || row.length < width) {
row = new byte[width];
}
image.getRaster().getDataElements(left, top + y, width, 1, row);
return row;
}
public byte[] getMatrix() {
int width = getWidth();
int height = getHeight();
int area = width * height;
byte[] matrix = new byte[area];
image.getRaster().getDataElements(left, top, width, height, matrix);
return matrix;
}
public boolean isCropSupported() {
return true;
}
public LuminanceSource crop(int left, int top, int width, int height) {
return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
}
public boolean isRotateSupported() {
return true;
}
public LuminanceSource rotateCounterClockwise() {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);
BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = rotatedImage.createGraphics();
g.drawImage(image, transform, null);
g.dispose();
int width = getWidth();
return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
}
}
上面這個類是谷歌提供的幫助類
第二個類:
package com.yc.qrcode;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Random;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QRCodeUtil {
private static final String CHARSET = "utf-8";
private static final String FORMAT_NAME = "JPG";
// 二維碼尺寸
private static final int QRCODE_SIZE = 300;
// LOGO寬度
private static final int WIDTH = 60;
// LOGO高度
private static final int HEIGHT = 60;
private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.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++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
if (imgPath == null || "".equals(imgPath)) {
return image;
}
// 插入圖片
QRCodeUtil.insertImage(image, imgPath, needCompress);
return image;
}
private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {
File file = new File(imgPath);
if (!file.exists()) {
System.err.println("" + imgPath + " 該文件不存在!");
return;
}
Image src = ImageIO.read(new File(imgPath));
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress) { // 壓縮LOGO
if (width > WIDTH) {
width = WIDTH;
}
if (height > HEIGHT) {
height = HEIGHT;
}
Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 繪製縮小後的圖
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
mkdirs(destPath);
// String file = new Random().nextInt(99999999)+".jpg";
// ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));
ImageIO.write(image, FORMAT_NAME, new File(destPath));
}
public static BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
return image;
}
public static void mkdirs(String destPath) {
File file = new File(destPath);
// 當文件夾不存在時,mkdirs會自動創建多層目錄,區別於mkdir.(mkdir如果父目錄不存在則會拋出異常)
if (!file.exists() && !file.isDirectory()) {
file.mkdirs();
}
}
public static void encode(String content, String imgPath, String destPath) throws Exception {
QRCodeUtil.encode(content, imgPath, destPath, false);
}
// 被註釋的方法
/*
* public static void encode(String content, String destPath, boolean
* needCompress) throws Exception { QRCodeUtil.encode(content, null, destPath,
* needCompress); }
*/
public static void encode(String content, String destPath) throws Exception {
QRCodeUtil.encode(content, null, destPath, false);
}
public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)
throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
ImageIO.write(image, FORMAT_NAME, output);
}
public static void encode(String content, OutputStream output) throws Exception {
QRCodeUtil.encode(content, null, output, false);
}
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;
}
public static String decode(String path) throws Exception {
return QRCodeUtil.decode(new File(path));
}
}
上面這個是工具類,裏面的方法我都寫了註釋,編碼和解碼方法都有多個重載方法,根據參數列表的不同,選擇不同的方法執行。其中生成的二維碼的圖片的名字可以隨機命名,也可以自己給定。我把隨機命名的方法註釋了,用的是給定圖片名稱。
主類:
package com.yc.qrcode;
public class QrCodeTest {
public static void main(String[] args) throws Exception {
// 存放在二維碼中的內容
String text = "我是小銘";
// 嵌入二維碼的圖片路徑
String imgPath = "G:/qrCode/dog.jpg";
// 生成的二維碼的路徑及名稱
String destPath = "G:/qrCode/qrcode/jam.jpg";
//生成二維碼
QRCodeUtil.encode(text, imgPath, destPath, true);
// 解析二維碼
String str = QRCodeUtil.decode(destPath);
// 打印出解析出的內容
System.out.println(str);
}
}
這裏面的代碼註釋我都寫得很清楚了,相信大家都可以看明白,其中:
生成二維碼方法:
QRCodeUtil.encode(text, imgPath, destPath, true);
text:編碼到二維碼中的內容,這裏是“我是小銘”
imgPath:要嵌入二維碼的圖片路徑,如果不寫或者爲null則生成一個沒有嵌入圖片的純淨的二維碼
destPath:生成的二維碼的存放路徑
true:表示將嵌入二維碼的圖片進行壓縮,如果爲“false”則表示不壓縮。
解析二維碼方法:
QRCodeUtil.decode(destPath);
destPath:將要解析的二維碼的存放路徑
該方法返回值爲String類型,即返回解析出的文字或者數字等。
下面給大家看看我的圖片路徑和效果:
下面是我的目錄結構:
這個圖片是我要嵌入二維碼的圖片,就是上圖中的G:/qrCode/dog.jpg這張圖片
下面這個是生成的二維碼存放的路徑:
下面是生成的二維碼的圖片,大家可以掃一掃,驗證一下:
下面是解析二維碼方法打印出來的數據:
如果你想讓別人掃描後跳轉一個頁面的話,直接在編碼的方法裏,將編碼內容改爲一個地址就可以了,這樣別人掃描二維碼後會自動跳轉。如下是我生成的二維碼,大家掃描後可以跳轉到我的博客:
二維碼存的信息越多,二維碼圖片也就越複雜,容錯率也就越低,識別率也越低,
並且二維碼能存的內容大小也是有限的(大概500個漢字左右)。
OK,到此,我們二維碼的生成與解析技術分享到此告一段落,
謝謝大家的閱讀,以後會給大家分享更多技術。
文章屬原創,如需引用,請註明出處。
————————————————
版權聲明:本文爲CSDN博主「jam_fanatic」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/jam_fanatic/article/details/82818857
動態二維碼生產方案:
1.生成二維碼並把值設置爲中轉地址(某隨機字符串,如www.a.com/qr/1d3bf5d8)
2.在redis中放入key: 1d3bf5d8, value: 真實二維碼跳轉地址,expire: 你想要的過期時間
用戶掃碼後,進入中轉地址,在redis中檢查是否存在這個key,如果存在則redirect到value,不存在則跳轉到統一的過期頁面。