java 生成二維碼、可帶LOGO、可去白邊


1.准備工作

  所需jar包:

JDK 1.6:

  commons-codec-1.11.jar

  core-2.2.jar

  javase-2.2.jar

JDK 1.7:

  commons-codec-1.11.jar

  core-3.2.1.jar

  javase-3.2.1.jar

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import org.apache.commons.codec.binary.Base64OutputStream;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

/**
 * 維碼處理工具類
 * @explain
 * @author Marydon
 * @creationTime 2018年11月23日下午3:16:55
 * @version 2.0
 * @since 1.0
 * @email marydon20170307@163.com
 */
public class QRcodeUtils {
   // base64編碼集
    public static final String CHARSET = "UTF-8";
    // 二維碼高度
    public static final int HEIGHT = 150;
    // 二維碼寬度
    public static final int WIDTH = 150;
    // 二維碼外邊距
    public static final int MARGIN = 0;
    // 二維碼圖片格式
    private static final String FORMAT = "jpg";
}  

2.生成二維碼

/**
 * 生成二維碼
 * @explain
 * @param data 字符串(二維碼實際內容)
 * @return
 */
public static BufferedImage createQRCode(String data) {
    return createQRCode(data, WIDTH, HEIGHT, MARGIN);
}
 
/**
 * 生成二維碼
 * @explain
 * @param data 字符串(二維碼實際內容)
 * @param width 寬
 * @param height 高
 * @param margin 外邊距,單位:像素,只能為整數,否則:報錯
 * @return BufferedImage
 */
public static BufferedImage createQRCode(String data, int width, int height, int margin) {
    BitMatrix matrix;
    try {
        // 設置QR二維碼參數
        Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(2);
        // 糾錯級別(H為最高級別)
        // L級:約可糾錯7%的數據碼字
        // M級:約可糾錯15%的數據碼字
        // Q級:約可糾錯25%的數據碼字
        // H級:約可糾錯30%的數據碼字
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 字符集
        hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
        // 邊框,(num * 10)
        hints.put(EncodeHintType.MARGIN, 0);// num
        // 編碼內容,編碼類型,生成圖片寬度,生成圖片高度,設置參數
        matrix = new MultiFormatWriter().encode(data, BarcodeFormat.QR_CODE,
                width, height, hints);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
    return MatrixToImageWriter.toBufferedImage(matrix);
}  

  說明:

  網上說,EncodeHintType.MARGIN的取值區間為[0,4],但是經過實際測試,當把它的值設為負整數、正整數的時候都不會報錯,但不是能是小數;

  去白邊,將EncodeHintType.MARGIN的值設為0的方法,根本無效,因為源碼中並沒有通過這個參數來設置白邊的寬度;

  大致過程:先根據內容生成二維碼,然后根據指定的寬高,對原來的二維碼進行放大或縮小,白色邊框的寬度=要求的寬高-放大或縮小后的二維碼的寬高。

  com\google\zxing\qrcode\QRCodeWriter.class源碼解讀

private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone)
  {
    ByteMatrix input = code.getMatrix();
    if (input == null)
      throw new IllegalStateException();

    int inputWidth = input.getWidth();
    int inputHeight = input.getHeight();
    int qrWidth = inputWidth + quietZone * 2;
    int qrHeight = inputHeight + quietZone * 2;
    int outputWidth = Math.max(width, qrWidth);
    int outputHeight = Math.max(height, qrHeight);

    int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
    // 有白色邊框的罪魁禍首:leftPadding和topPadding 
    int leftPadding = (outputWidth - inputWidth * multiple) / 2;
    int topPadding = (outputHeight - inputHeight * multiple) / 2;

    BitMatrix output = new BitMatrix(outputWidth, outputHeight);

    int inputY = 0; for (int outputY = topPadding; inputY < inputHeight; )
    {
      int inputX = 0; for (int outputX = leftPadding; inputX < inputWidth; ) {
        if (input.get(inputX, inputY) == 1)
          output.setRegion(outputX, outputY, multiple, multiple);
        ++inputX; outputX += multiple;
      }
      ++inputY; outputY += multiple;
    }

    return output;
  }  

3.去白邊

  在createQRCode()方法中添加如下代碼:

// 裁減白邊(強制減掉白邊)
if (margin == 0) {
    bitMatrix = deleteWhite(bitMatrix);
}

  具體實現方法:

/**
 * 強制將白邊去掉
 * @explain
 *  雖然生成二維碼時,已經將margin的值設為了0,但是在實際生成二維碼時有時候還是會生成白色的邊框,邊框的寬度為10px;
 *  白邊的生成還與設定的二維碼的寬、高及二維碼內容的多少(內容越多,生成的二維碼越密集)有關;
 *  因為是在生成二維碼之后,才將白邊裁掉,所以裁剪后的二維碼(實際二維碼的寬、高)肯定不是你想要的尺寸,只能自己一點點試嘍!
 * @param matrix
 * @return 裁剪后的二維碼(實際二維碼的大小)
 */
private static BitMatrix deleteWhite(BitMatrix matrix) {
    int[] rec = matrix.getEnclosingRectangle();
    int resWidth = rec[2] + 1;
    int resHeight = rec[3] + 1;

    BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
    resMatrix.clear();
    for (int i = 0; i < resWidth; i++) {
        for (int j = 0; j < resHeight; j++) {
            if (matrix.get(i + rec[0], j + rec[1]))
                resMatrix.set(i, j);
        }
    }
    
    int width = resMatrix.getWidth();
    int height = resMatrix.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, resMatrix.get(x, y) ? 0 : 255);// 0-黑色;255-白色
        }
    }
    
    return resMatrix;
}

  說明:這種方法只能去除普通的二維碼的白邊,不能去除帶LOGO的二維碼的白邊。

4.生成帶logo的二維碼

/**
 * 生成帶logo的二維碼
 * @explain 寬、高、外邊距使用定義好的值
 * @param data 字符串(二維碼實際內容)
 * @param logoFile logo圖片文件對象
 * @return BufferedImage
 */
public static BufferedImage createQRCodeWithLogo(String data, File logoFile) {
    return createQRCodeWithLogo(data, WIDTH, HEIGHT, MARGIN, logoFile);
}
 
/**
 * 生成帶logo的二維碼
 * @explain 自定義二維碼的寬和高
 * @param data 字符串(二維碼實際內容)
 * @param width 寬
 * @param height 高
 * @param logoFile logo圖片文件對象
 * @return BufferedImage
 * @return
 */
public static BufferedImage createQRCodeWithLogo(String data, int width, int height, int margin, File logoFile) {
    BufferedImage combined = null;
    try {
        BufferedImage qrcode = createQRCode(data, width, height, margin);
        BufferedImage logo = ImageIO.read(logoFile);
        int deltaHeight = height - logo.getHeight();
        int deltaWidth = width - logo.getWidth();
        combined = new BufferedImage(height, width, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) combined.getGraphics();
        g.drawImage(qrcode, 0, 0, null);
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));
        g.drawImage(logo, (int) Math.round(deltaWidth / 2), (int) Math.round(deltaHeight / 2), null);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
    return combined;
}  

5.以什么樣的方式返回二維碼

/**
 * 將二維碼信息寫入文件中
 * @explain
 * @param image
 * @param file 用於存儲二維碼的文件對象
 */
public static void writeToFile(BufferedImage image, File file) {
    try {
        ImageIO.write(image, FORMAT, file);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

/**
 * 將二維碼信息寫入流中
 * @explain
 * @param image
 * @param 文件stream
 */
public static void writeToStream(BufferedImage image, OutputStream stream) {
    try {
        ImageIO.write(image, FORMAT, stream);
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

/**
 * 獲取base64格式的二維碼
 * @explain 圖片類型:jpg
 *  展示:<img src="data:image/jpeg;base64,base64Str"/>
 * @param image
 * @return base64
 */
public static String writeToString(BufferedImage image) {
    String base64Str;
    try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        OutputStream os = new Base64OutputStream(bos);
        writeToStream(image, os);
        // 按指定字符集進行轉換並去除換行符
        base64Str = bos.toString(CHARSET).replace("\r\n", "");
    } catch (Exception e) {
        throw new RuntimeException(e.getMessage(), e);
    }
    return base64Str;
}  

6.base64生成圖片

/**
 * 將base64轉成圖片
 * @explain
 * @param base64 base64格式圖片
 * @param file 用於存儲二維碼的文件對象
 */
public static void base64ToImage(String base64, File file) {
	FileOutputStream os;
	try {
		Base64 d = new Base64();
		byte[] bs = d.decode(base64);
		os = new FileOutputStream(file.getAbsolutePath());
		os.write(bs);
		os.close();
	} catch (Exception e) {
		throw new RuntimeException(e.getMessage(), e);
	}
}

7.測試

  見文末推薦

2018/11/29

  添加二維碼邊框參數設置;

  代碼優化。

2018/11/30

  添加去除白邊功能。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM