1. 程式人生 > >Java實現高可定製的二維碼生成

Java實現高可定製的二維碼生成

開篇廢話

不是第一次寫二維碼生成的程式碼,但是是第一次把它整理清晰並做成高可定製的。具體生成實現都是和網上的差不多用了com.swetake.util.Qrcode進行關鍵處理。但是這篇文章的不同之處在於不是為了簡單的實現,而是為了實現高可定製的功能。很多時候要用二維碼,但是實際上二維碼的場景還是很多的。列印在紙巾上的是二維碼,做成大海報的也是二維碼,嵌入logo的是二維碼,不嵌入logo的也是二維碼。

為了讓這個工具類使用場景更廣,特地封裝了一個配置類,根據配置進行更多的處理,最終定製二維碼的生成。

額,個人比較懶,就不貼全部程式碼了,這裡之列出一些關鍵實現,沒啥技術難點,只是註釋得多,方便理解。

ok,開始……

關鍵程式碼

1、模式設定

// 設定編碼容錯級別
qrcode.setQrcodeErrorCorrect(c.getLevel());
// 設定編碼模式
qrcode.setQrcodeVersion(c.getEncodeMode());
// 設定編碼版本
qrcode.setQrcodeVersion(c.getVersion());

2、字元轉化

// 轉化字串為byte陣列,用於二維碼編碼作為引數
byte[] buff = source.getBytes(charset);
boolean[][] bRect;
try {
	// 進行編碼處理,超長將會在這裡丟擲異常
	bRect = qrcode.calQrcode(buff);
} catch (Exception e) {
	// 這裡如果出現異常,一般是超長了,所以這裡丟擲異常
	throw new QRCodeGenerateException("Generate qrcode exception."
			+ "Please check your generate config and check the source string."
			+ "The exception offen by the source string is too long." 
			+ "The source string is : " + source 
			+ "[" + source.getBytes(charset).length + "]", e);
}

3、繪圖設定

// 預設使用BufferedImage.TYPE_INT_RGB生成影象,暫不支援自定義
BufferedImage bi = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_RGB);
// 建立影象
Graphics2D g = bi.createGraphics();
if(c.getBackgroundColor() != null){
	// 設定背景色為白色
	g.setBackground(c.getBackgroundColor());
}
// 清空區域
g.clearRect(0, 0, c.getWidth(), c.getHeight());
// 設定前景色為黑色,即填充色
g.setColor(c.getColor());

4、進行繪圖

try {
	// 根據byte矩陣進行繪圖
	for (int i = 0; i < bRect.length; i++) {
		for (int j = 0; j < bRect.length; j++) {
			if (bRect[j][i]) {
				// 這裡的fill影響邊
				g.fillRect(j * c.getOffset() + c.getPixoff(), i * c.getOffset() + c.getPixoff(), c.getOffset(), c.getOffset());
			}
		}
	}
	//如果設定嵌入的logo檔案不為空,則執行嵌入操作
	if(logo != null){
		insertLogo(logo, c, g);
	}
	//這裡不進行異常處理,直接丟擲,但要進行釋放處理
} finally {
	// 釋放影象
	g.dispose();
	// 重新整理快取區
	bi.flush();
}

5、儲存圖片

//根據uri協議獲取對應輸出流
OutputStream os = getOutputStream(target);
//寫入io,預設jepg壓縮,暫不支援其他格式影象
ImageIO.write(bi, c.getFormat(), os);

其他功能實現

1、插入logo的實現

/**
 * 插入logo到二維碼中央
 * @param logo logo源
 * @param config 二維碼生成配置
 * @param g 繪製Graphics2D物件
 * @throws IOException 繪製時可能產生的IO一場
 * @throws MalformedURLException 解析logo源可能出現的URL地址錯誤異常
 * @see #build(URI, String, String, URI, QRCodeGenerateConfig)
 */
private void insertLogo(URI logo, QRCodeGenerateConfig config, Graphics2D g) throws IOException, MalformedURLException {
	//獲取Image物件。
	Image img = getLogoResource(logo);
	//定義logo繪製引數
	int logoX,logoY,logoW,logoH;
	//獲取logo大小
	logoW = config.getLogoWidth();
	logoH = config.getLogoHeight();
	//計算logo座標
	logoX = (config.getWidth() - logoW) / 2;
	logoY = (config.getHeight() - logoH) / 2;
	//這裡不驗證實際數值是否為負數,因此如果由配置不當造成的異常,就不管了
	//繪製logo
	try{
		g.drawImage(img, logoX, logoY, logoW, logoH, null);
	}catch(Exception ee){
		// 這裡如果出現異常,一般是超長了,所以這裡丟擲異常
		throw new QRCodeGenerateException("Draw logo exception,please check your generate config.",ee);
	}
}

2、getLogoResource的實現

/**
 * 根據logo源URI獲取logo的Image物件
 * @param logo logo源URI
 * @return Image物件
 * @throws IOException 獲取時可能產生的IO異常
 * @throws MalformedURLException 解析URL類logo資源時可能出現的URL地址錯誤異常
 * @see #build(URI, String, String, URI, QRCodeGenerateConfig)
 */
private Image getLogoResource(URI logo) throws IOException, MalformedURLException {
	Image img;
	if ("http".equalsIgnoreCase(logo.getScheme())) {
		img = ImageIO.read(logo.toURL());
	} else if ("https".equalsIgnoreCase(logo.getScheme())) {
		img = ImageIO.read(logo.toURL());
	} else if ("file".equalsIgnoreCase(logo.getScheme())) {
		File f = new File(logo);
		img = ImageIO.read(f);
	} else {
		//不支援的協議直接丟擲異常
		throw new QRCodeGenerateException("Insert logo image into qrcode exception,the uri scheme is not support.We only support the scheme such as http,https and file.\n" + logo.toString());
	}
	return img;
}

3、getOutputStream的實現

/**
 * 根據目標URI地址獲取輸出流
 * @param target 目標URI
 * @return 輸出流
 * @throws MalformedURLException 解析目標URI可能出現的URL地址錯誤異常
 * @throws IOException 開啟輸出流可能出現的IO異常
 * @throws FileNotFoundException 開啟檔案型別的URI時可能出現的檔案不存在異常
 * @see #build(URI, String, String, URI, QRCodeGenerateConfig)
 */
private OutputStream getOutputStream(URI target) throws MalformedURLException, IOException,
		FileNotFoundException {
	OutputStream os;
	if ("http".equalsIgnoreCase(target.getScheme())) {
		os = getHttpOutputStream(target);
	} else if ("https".equalsIgnoreCase(target.getScheme())) {
		os = getHttpOutputStream(target);
	} else if ("file".equalsIgnoreCase(target.getScheme())) {
		File f = new File(target);
		os = new FileOutputStream(f);
	} else {
		//不支援的協議直接丟擲異常
		throw new QRCodeGenerateException("Save qrcode image exception,the uri scheme is not support.We only support the scheme such as http,https and file.\n" + target.toString());
	}
	return os;
}

相關定義

1、引用

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import javax.imageio.ImageIO;
import com.swetake.util.Qrcode;

2、核心實現方法定義

/**
 * 生成帶Logo的二維碼,自定義配置的生成方式
 * @param logo 嵌入中間的logo來源Uri
 * @param source 源字串
 * @param charset 編碼
 * @param target 生成的二維碼圖片要儲存的目標URI
 * @param config 二維碼生成配置
 * @throws QRCodeGenerateException 二維碼生成異常
 * @throws UnsupportedEncodingException 指定的編碼不支援
 * @throws IOException 二維碼圖片儲存產生的IO異常
 */
public void build(URI logo, String source, String charset, URI target, QRCodeGenerateConfig config)
		throws QRCodeGenerateException, UnsupportedEncodingException, IOException;

3、config類關鍵欄位

/**
 * 二維碼版本,預設為5
 */
private int version = 5;
/**
 * 二維碼編碼模式,預設為B
 */
private QRCodeEncodeMode encodeMode = QRCodeEncodeMode.B;
	
/**
 * 二維碼容錯級別,預設為M
 */
private QRCodeLevel level = QRCodeLevel.M;
/**
 * 生成二維碼的寬度
 */
private int width = 115;
/**
 * 生成二維碼的高度
 */
private int height = 115;
/**
 * 生成二維碼嵌入的logo寬度
 */
private int logoWidth = 30;
/**
 * 生成二維碼嵌入的logo高度
 */
private int logoHeight = 30;
/**
 * 畫素大小
 */
private int offset = 3;
/**
 * 偏移量
 */
private int pixoff = 2;
/**
 * 二維碼影象儲存格式
 */
private String format = "jpeg";
/**
 * 二維碼影象背景色
 */
private Color backgroundColor = Color.WHITE;
/**
 * 二維碼影象前景色
 */
private Color color = Color.BLACK;

其它說明

1、建議的寬高及偏移量配置比例[適用預設版本和容錯級別*]

二維碼影象本身寬度和高度 —— 1:1
二維碼LOGO影象寬度和高度 —— 1:1
二維碼影象本身寬度和LOGO影象寬度 —— 23:6[115:30]
二維碼影象本身高度和LOGO影象高度 —— 23:6[115:30]
二維碼影象本身寬度和畫素大小 —— 115:3
二維碼影象本身高度和畫素大小 —— 115:3
二維碼影象本身寬度和偏移量 —— 115:2
二維碼影象本身高度和偏移量 —— 115:2
二維碼畫素大小和偏移量 —— 3:2

2、參考資料

備註

預設配置說明

版本5,容錯級別為M,寬高115,logo寬高30,畫素大小3,偏移量2,生成jpeg格式圖片,背景色白色,畫筆顏色黑色

說明

由於預設寬高是115,因此打印出來可能會比較小,如果需要列印大圖片,則需要參考比例進行影象放大。

結果示例圖: