二維碼生成與解析

百度盤工具生成二維碼:
http://pan.baidu.com/share/qrcode?w=150&h=150&url=https://qr.alipay.com/bax06945fl00z4vfiovp20c6

一.maven配置

   <!-- 生成二維碼zxing工具包-->
    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>core</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>javase</artifactId>
        <version>3.1.0</version>
    </dependency>

二.二維碼類

package com.corefire.util;
/**
 * 二維碼生成類(可生成二維碼和條形碼)
 */
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.http.client.ResponseHandler;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.Binarizer;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import javax.servlet.http.HttpServletResponse;

public class QRCodeUtil {

    // 這幾項可以由其他調(diào)用的類設(shè)置依啰,因此是public static的
    public static int BARCODE_WIDTH = 80;
    public static int QRCODE_WIDTH = 200;
    public static String FORMAT = "jpg";// 生成的圖片格式
    public static int BLACK = 0x000000;// 編碼的顏色
    public static int WHITE = 0xFFFFFF;// 空白的顏色

    // 二維碼中間的圖像配置阎曹。注意,由于二維碼的容錯率有限花竞,因此中間遮擋的面積不要太大驮审,否則可能解析不出來鲤竹。
    private static int ICON_WIDTH = (int)(QRCODE_WIDTH / 6);
    private static int HALF_ICON_WIDTH = ICON_WIDTH / 2;
    private static int FRAME_WIDTH = 2;// Icon四周的邊框?qū)挾?    
    // 二維碼讀碼器和寫碼器
    private static final MultiFormatWriter WRITER = new MultiFormatWriter();
    private static final MultiFormatReader READER = new MultiFormatReader();

    // 測試
    public static void main(String[] args) throws Exception {
        /**
         * 二維碼測試。
         */
        String iconPath = "/home/lxf/qrcode/icon.jpg";
        String content = "http://www.baidu.com";
        File qrCode = new File("/home/lxf/qrcode/qrcode." + FORMAT);
        File qrCodeWithIcon = new File("/home/lxf/qrcode/qrcode_img." + FORMAT);
        // 生成二維碼
        writeToFile(createQRCode(content), qrCode);
        // 生成帶圖標的二維碼
        writeToFile(createQRCodeWithIcon(content, iconPath), qrCodeWithIcon);
        // 解析二維碼
        System.out.println(parseImage(qrCode));
        // 解析帶圖標的二維碼
        System.out.println(parseImage(qrCodeWithIcon));

        // 編碼成字節(jié)數(shù)組
        byte[] data = createQRCodeToBytes(content);
        String result = parseQRFromBytes(data);
        System.out.println(result);

        /**
         * 一維碼測試。
         */
        String barCodeContent="6936983800013";
        File barCode = new File("C:\\BarCode." + FORMAT);
        // 生成一維碼
        writeToFile(createBarCode(barCodeContent), barCode);
        // 解析一維碼
        System.out.println(parseImage(barCode));
    }
    

    /**
     * 將String編碼成二維碼的圖片后黍析,使用字節(jié)數(shù)組表示,便于傳輸屎开。
     * 
     * @param content
     * @return
     * @throws WriterException
     * @throws IOException
     */
    public static byte[] createQRCodeToBytes(String content) 
            throws WriterException, IOException {
        BufferedImage image = createQRCode(content);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, FORMAT, os);
        return os.toByteArray();
    }

    /**
     * 把一個String編碼成二維碼的BufferedImage.
     * 
     * @param content
     * @return
     * @throws WriterException
     */
    public static final BufferedImage createQRCode(String content) 
            throws WriterException {
        // 長和寬一樣橄仍,所以只需要定義一個SIZE即可
        BitMatrix matrix = WRITER.encode(
                content, BarcodeFormat.QR_CODE, QRCODE_WIDTH, QRCODE_WIDTH);
        return toBufferedImage(matrix);
    }
    
    /**
     * 編碼字符串為二維碼,并在該二維碼中央插入指定的圖標牍戚。
     * @param content
     * @param iconPath
     * @return
     * @throws WriterException
     */
    public static final BufferedImage createQRCodeWithIcon(
            String content, String iconPath) throws WriterException {
        BitMatrix matrix = WRITER.encode(
                content, BarcodeFormat.QR_CODE, QRCODE_WIDTH, QRCODE_WIDTH);
        // 讀取Icon圖像
        BufferedImage scaleImage = null;
        try {
            scaleImage = scaleImage(iconPath, ICON_WIDTH, ICON_WIDTH, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        int[][] iconPixels = new int[ICON_WIDTH][ICON_WIDTH];
        for (int i = 0; i < scaleImage.getWidth(); i++) {
            for (int j = 0; j < scaleImage.getHeight(); j++) {
                iconPixels[i][j] = scaleImage.getRGB(i, j);
            }
        }

        // 二維碼的寬和高
        int halfW = matrix.getWidth() / 2;
        int halfH = matrix.getHeight() / 2;
        
        // 計算圖標的邊界:
        int minX = halfW - HALF_ICON_WIDTH;//左
        int maxX = halfW + HALF_ICON_WIDTH;//右
        int minY = halfH - HALF_ICON_WIDTH;//上
        int maxY = halfH + HALF_ICON_WIDTH;//下
        
        int[] pixels = new int[QRCODE_WIDTH * QRCODE_WIDTH];

        // 修改二維碼的字節(jié)信息侮繁,替換掉一部分為圖標的內(nèi)容。
        for (int y = 0; y < matrix.getHeight(); y++) {
            for (int x = 0; x < matrix.getWidth(); x++) {
                // 如果點在圖標的位置如孝,用圖標的內(nèi)容替換掉二維碼的內(nèi)容
                if (x > minX && x < maxX && y > minY && y < maxY) {
                    int indexX = x - halfW + HALF_ICON_WIDTH;
                    int indexY = y - halfH + HALF_ICON_WIDTH;
                    pixels[y * QRCODE_WIDTH + x] = iconPixels[indexX][indexY];
                }
                // 在圖片四周形成邊框
                else if ((x > minX - FRAME_WIDTH && x < minX + FRAME_WIDTH 
                       && y > minY - FRAME_WIDTH && y < maxY + FRAME_WIDTH)
                      || (x > maxX - FRAME_WIDTH && x < maxX + FRAME_WIDTH 
                       && y > minY - FRAME_WIDTH && y < maxY + FRAME_WIDTH)
                      || (x > minX - FRAME_WIDTH && x < maxX + FRAME_WIDTH 
                       && y > minY - FRAME_WIDTH && y < minY + FRAME_WIDTH)
                      || (x > minX - FRAME_WIDTH && x < maxX + FRAME_WIDTH 
                       && y > maxY - FRAME_WIDTH && y < maxY + FRAME_WIDTH)) {
                    pixels[y * QRCODE_WIDTH + x] = WHITE;
                }
                else {
                    // 這里是其他不屬于圖標的內(nèi)容宪哩。即為二維碼沒有被圖標遮蓋的內(nèi)容,用矩陣的值來顯示顏色第晰。
                    pixels[y * QRCODE_WIDTH + x] = matrix.get(x, y) ? BLACK : WHITE;
                }
            }
        }

        // 用修改后的字節(jié)數(shù)組創(chuàng)建新的BufferedImage.
        BufferedImage image = new BufferedImage(
                QRCODE_WIDTH, QRCODE_WIDTH, BufferedImage.TYPE_INT_RGB);
        image.getRaster().setDataElements(0, 0, QRCODE_WIDTH, QRCODE_WIDTH, pixels);

        return image;
    }

    /**
     * 從一個二維碼圖片的字節(jié)信息解碼出二維碼中的內(nèi)容锁孟。
     * 
     * @param data
     * @return
     * @throws ReaderException
     * @throws IOException
     */
    public static String parseQRFromBytes(byte[] data) 
            throws ReaderException, IOException {
        ByteArrayInputStream is = new ByteArrayInputStream(data);
        BufferedImage image = ImageIO.read(is);
        return parseImage(image);
    }

    /**
     * 從一個圖片文件中解碼出二維碼中的內(nèi)容。
     * 
     * @param file
     * @return 解析后的內(nèi)容茁瘦。
     * @throws IOException
     * @throws ReaderException
     */
    public static final String parseImage(File file) 
            throws IOException, ReaderException {
        BufferedImage image = ImageIO.read(file);
        return parseImage(image);
    }

    /**
     * 將字符串編碼成一維碼(條形碼)品抽。
     * @param content
     * @return
     * @throws WriterException
     * @throws IOException
     */
    public static BufferedImage createBarCode(String content) 
            throws WriterException, IOException {
        MultiFormatWriter writer = new MultiFormatWriter();
        // 一維碼的寬>高。這里我設(shè)置為 寬:高=2:1
        BitMatrix matrix = writer.encode(content, 
                BarcodeFormat.EAN_13, BARCODE_WIDTH * 3, BARCODE_WIDTH);
        return toBufferedImage(matrix);
    }

    /**
     * 從圖片中解析出一維碼或者二維碼的內(nèi)容甜熔。如果解析失敗圆恤,則拋出NotFoundException。
     * @param image
     * @return
     * @throws NotFoundException
     */
    public static final String parseImage(BufferedImage image) 
            throws NotFoundException {
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        Binarizer binarizer = new HybridBinarizer(source);
        BinaryBitmap bitmap = new BinaryBitmap(binarizer);
        Result result = READER.decode(bitmap);
        // 這里丟掉了Result中其他一些數(shù)據(jù)
        return result.getText();
    }

    /**
     * 將BufferedImage對象輸出到指定的文件中腔稀。
     * 
     * @param image
     * @param destFile
     * @throws IOException
     */
    public static final void writeToFile(BufferedImage image, File destFile) 
            throws IOException {
        ImageIO.write(image, FORMAT, destFile);
    }
    
    /**
     * 將BufferedImage對象直接response
     * @param image
     * @param response
     * @throws IOException
     */
    public static final void showQrcode(BufferedImage image, HttpServletResponse response) throws IOException
    {
        ImageIO.write(image, FORMAT, response.getOutputStream());
    }
    
    /**
     * 將一個BitMatrix對象轉(zhuǎn)換成BufferedImage對象
     * 
     * @param matrix
     * @return
     */
    private static BufferedImage toBufferedImage(BitMatrix matrix) {
        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++) {
                image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
            }
        }
        return image;
    }
    
    /**
     * 把傳入的原始圖像按高度和寬度進行縮放盆昙,生成符合要求的圖標。
     * 
     * @param srcImageFile 源文件地址
     * @param height 目標高度
     * @param width 目標寬度
     * @param hasFiller 比例不對時是否需要補白:true為補白; false為不補白;
     * @throws IOException
     */
    private static BufferedImage scaleImage(String srcImageFile, int height, 
            int width, boolean hasFiller) throws IOException {
        double ratio = 0.0; // 縮放比例
        File file = new File(srcImageFile);
        BufferedImage srcImage = ImageIO.read(file);
        Image destImage = srcImage.getScaledInstance(
                width, height, BufferedImage.SCALE_SMOOTH);
        // 計算比例
        if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) {
            if (srcImage.getHeight() > srcImage.getWidth()) {
                ratio = (new Integer(height)).doubleValue() / srcImage.getHeight();
            } else {
                ratio = (new Integer(width)).doubleValue() / srcImage.getWidth();
            }
            AffineTransformOp op = new AffineTransformOp(
                    AffineTransform.getScaleInstance(ratio, ratio), null);
            destImage = op.filter(srcImage, null);
        }
        if (hasFiller) {// 補白
            BufferedImage image = new BufferedImage(
                    width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphic = image.createGraphics();
            graphic.setColor(Color.white);
            graphic.fillRect(0, 0, width, height);
            if (width == destImage.getWidth(null)) {
                graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, 
                    destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
            } else {
                graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, 
                    destImage.getWidth(null), destImage.getHeight(null), Color.white, null);
            }
            graphic.dispose();
            destImage = image;
        }
        return (BufferedImage) destImage;
    }
}

三.使用servlet直接將二維碼動態(tài)輸出

web.xml配置

  <!-- 生成二維碼到j(luò)sp頁面 2017-07-04 liangxifeng -->
  <servlet>  
    <servlet-name>QrcodeServlet</servlet-name>  
    <servlet-class>com.corefire.servlet.QrcodeServlet</servlet-class>  
  </servlet>  
    <servlet-mapping>  
    <servlet-name>QrcodeServlet</servlet-name>  
     <url-pattern>/QrcodeServlet</url-pattern>  
  </servlet-mapping> 
  <!--
  <servlet-mapping>  
    <servlet-name>QrcodeServlet</servlet-name>  
     <url-pattern>/qrcode.jsp</url-pattern>  
  </servlet-mapping> 
   -->  
</web-app>

QrcodeServlet配置

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        request.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        /**
         * 二維碼測試焊虏。
         */
        String iconPath = "/home/lxf/qrcode/icon.jpg";
        String content = "http://www.baidu.com";
        //一. 生成二維碼圖片直接輸出
        /*
        try {
            QRCodeUtil.showQrcode(QRCodeUtil.createQRCode(content), response);
        } catch (WriterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
        // 二.生成帶圖標的二維碼直接輸出
        try {
            QRCodeUtil.showQrcode(QRCodeUtil.createQRCodeWithIcon(content, iconPath), response);
        } catch (WriterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

四.使用

<img alt="img"  width="800" height="800"  src="http://localhost:8081/wxpay/QrcodeServlet">

可以將二維碼內(nèi)容作為參數(shù)傳遞淡喜,實現(xiàn)動態(tài)效果!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诵闭,一起剝皮案震驚了整個濱河市炼团,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌疏尿,老刑警劉巖瘟芝,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異润歉,居然都是意外死亡模狭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門踩衩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嚼鹉,“玉大人,你說我怎么就攤上這事驱富∶啵” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵褐鸥,是天一觀的道長线脚。 經(jīng)常有香客問我,道長叫榕,這世上最難降的妖魔是什么浑侥? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮晰绎,結(jié)果婚禮上寓落,老公的妹妹穿的比我還像新娘。我一直安慰自己荞下,他們只是感情好伶选,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尖昏,像睡著了一般仰税。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抽诉,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天陨簇,我揣著相機與錄音,去河邊找鬼迹淌。 笑死塞帐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的巍沙。 我是一名探鬼主播葵姥,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼句携!你這毒婦竟也來了榔幸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矮嫉,失蹤者是張志新(化名)和其女友劉穎削咆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蠢笋,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡拨齐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了昨寞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞻惋。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡厦滤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歼狼,到底是詐尸還是另有隱情掏导,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布羽峰,位于F島的核電站锋叨,受9級特大地震影響哟忍,放射性物質(zhì)發(fā)生泄漏隔节。R本人自食惡果不足惜彪薛,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坯汤。 院中可真熱鬧虐唠,春花似錦、人聲如沸玫霎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庶近。三九已至翁脆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鼻种,已是汗流浹背反番。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叉钥,地道東北人罢缸。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像投队,于是被迫代替她去往敵國和親枫疆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容