通過(guò) 生成二維碼之 Java (Google zxing) 篇 我們可以實(shí)現(xiàn)簡(jiǎn)單二維碼的生成, 但是二維碼顯示卻過(guò)于單調(diào), 本文變講述如何利用 thumbnailator 為我們的二維碼添加 LOGO
thumbnailator 是一個(gè)縮略圖工具類庫(kù), 但它除了能縮略圖片外, 還提供裁剪, 旋轉(zhuǎn), 水印等功能, 此次我們便借助它的水印 API 實(shí)現(xiàn)以上需求
項(xiàng)目環(huán)境
- jdk1.8
- thumbnailator
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
操作步驟
-
準(zhǔn)備一張二維碼圖片, 一張 logo 圖片
String qrCodeFilePath = "src/qrCode.jpg"; String logoFilePath = "src/logo.jpg";
-
根據(jù)文件路徑生成一個(gè)圖片構(gòu)造對(duì)象(
Thumbnails.of()
方法還可以接收BufferedImage
,InputStream
,URL
,String
的可變參數(shù)類型)Thumbnails.Builder<File> builder = Thumbnails.of(new File(qrCodeFilePath));
-
創(chuàng)建一個(gè)水印對(duì)象, 水印對(duì)象需要三個(gè)參數(shù)
Position position
,BufferedImage watermarkImg
,float opacity
, 其中Position
是水印的坐標(biāo),BufferedImage
是水印圖片,opacity
是不透明度, 值為 [0-1] 之間, 1 代表不透明.BufferedImage bufferedImage = ImageIO.read(new File(logoFilePath)); //Positions 實(shí)現(xiàn)了 Position 并提供九個(gè)坐標(biāo), 分別是 上左, 上中, 上右, 中左, 中中, 中右, 下左, 下中, 下右 我們使用正中的位置 Watermark watermark = new Watermark(Positions.CENTER, bufferedImage, 1F);
-
為二維碼設(shè)置水印, 并設(shè)置縮略比例為 1(即不壓縮), 輸出到一個(gè)新文件中(
outputFormat()
為指定輸出格式, 如: jpg,png)builder.watermark(watermark).scale(1F).toFile(new File("src/logoQrCode.png"));
-
生成后的二維碼
通過(guò)以上方法我們能夠簡(jiǎn)單的為二維碼添加 LOGO, 但是實(shí)際使用時(shí)遠(yuǎn)沒(méi)有這樣簡(jiǎn)單, 有以下幾個(gè)問(wèn)題
-
logo 圖片的尺寸可能并不固定, 可能有大有小, 這樣就會(huì)導(dǎo)致 logo 在二維碼中太小或太大
這時(shí)我們可以在創(chuàng)建BufferedImage
時(shí)將原圖壓縮 / 放大至指定尺寸, 也可以使用二維碼的尺寸乘以一定比例//forceSize(int width, int height) 指將圖片強(qiáng)制壓縮為指定寬高, 如不強(qiáng)制, 可使用 size(int width, int height) BufferedImage bufferedImage = Thumbnails.of(new File(logoFilePath)).forceSize(120, 120).asBufferedImage();
-
二維碼圖片, logo 圖片可能不是一個(gè)圖片文件, 同時(shí)生成好之后的圖片希望直接輸出在頁(yè)面上
Thumbnails.of()
可以接收BufferedImage
,InputStream
,URL
,String
,File
的可變 (** 批量處理需要 **) 參數(shù)類型
ImageIO.read()
可以接收File
,ImageInputStream
,InputStream
,URL
參數(shù)類型
以下為 SpringMVC 動(dòng)態(tài)生成帶 LOGO 二維碼示例,QRCodeUtil.toBufferedImage()
具體實(shí)現(xiàn)請(qǐng)看 生成二維碼之 Java (Google zxing) 篇/** * @param content 二維碼內(nèi)容 * @param logoUrl logo 鏈接 */ @RequestMapping(value = "/qrcode") public void qrcode(String content, String logoUrl, @RequestParam(defaultValue = "300") int width, @RequestParam(defaultValue = "300") int height,HttpServletResponse response) { ServletOutputStream outputStream = null; try { outputStream = response.getOutputStream(); // 根據(jù) QRCodeUtil.toBufferedImage() 返回的 BufferedImage 創(chuàng)建圖片構(gòu)件對(duì)象 Thumbnails.Builder<BufferedImage> builder = Thumbnails.of(QRCodeUtil.toBufferedImage(content, width, height)); // 將 logo 的尺寸設(shè)置為二維碼的 30% 大小, 可以自己根據(jù)需求調(diào)節(jié) BufferedImage logoImage = Thumbnails.of(new URL(logoUrl)).forceSize((int) (width * 0.3), (int) (height * 0.3)).asBufferedImage(); // 設(shè)置水印位置(居中), 水印圖片 BufferedImage, 不透明度(1F 代表不透明) builder.watermark(Positions.CENTER, logoImage, 1F).scale(1F); // 此處需指定圖片格式, 否者報(bào) Output format not specified 錯(cuò)誤 builder.outputFormat("png").toOutputStream(outputStream); } catch (Exception e) { e.printStackTrace(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
/** * 返回一個(gè) BufferedImage 對(duì)象 * @param content 二維碼內(nèi)容 * @param width 寬 * @param height 高 */ public static BufferedImage toBufferedImage(String content, int width, int height) throws WriterException, IOException { BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); return MatrixToImageWriter.toBufferedImage(bitMatrix); }
拓展思考
以上案例可知 thumbnailator 水印功能的強(qiáng)大. 然而除了實(shí)現(xiàn)以上案例外, 我們也可以根據(jù)水印功能實(shí)現(xiàn)另外一種需求.
比如公司現(xiàn)在有一個(gè)推廣需求, 讓用戶為我們的產(chǎn)品進(jìn)行宣傳, 達(dá)到多少次就進(jìn)行獎(jiǎng)勵(lì). 傳統(tǒng)的實(shí)現(xiàn)方式是一段文字再配上一個(gè)專屬鏈接. 但是此方式往往太過(guò)單調(diào), 枯燥. 這個(gè)時(shí)候如果我們讓設(shè)計(jì)提供一張內(nèi)容豐富, 帶有二維碼的宣傳圖片, 然后根據(jù)專屬鏈接生成二維碼與宣傳圖片進(jìn)行組合. 這時(shí)用戶就有了一張自己的專屬宣傳圖片. 此時(shí)通過(guò)直接宣傳專屬圖片往往會(huì)比文字加鏈接有著更好的效果.
注: 此功能在圖片合成時(shí)需要對(duì)二維碼的位置進(jìn)行定位, 此時(shí)可使用 Position
的實(shí)現(xiàn)類 Coordinate
完成
使用如下:
//Coordinate 存在一個(gè) Coordinate(int x, int y) 構(gòu)造函數(shù), x 為水印距離底圖左邊的像素, y 為上邊
BufferedImage bufferedImage = ImageIO.read(new File(logoFilePath));
Watermark watermark = new Watermark(new Coordinate(100, 100), bufferedImage, 1F);