1. 程式人生 > 實用技巧 >java呼叫pyzbar解析base64型的二維碼實踐優化

java呼叫pyzbar解析base64型的二維碼實踐優化

  1. Java程式碼
    UserController.java
package com.yang.springbootlucene.controller;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @author:yjx
 * @description:
 * @date:2019/11/28 15:29
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Value("${pyzbar.script}")
    private String scriptPath;

    private static final String BASE_64_PREFIX = "data:image/png;base64,";

    /**
     * 這種方式在原始碼中成功呼叫,但達成jar包後,找不到指令碼路徑,所以指令碼必須放在外面
     */
    @Deprecated
    private static String pyZbarScriptPath;

    static {
        ClassPathResource pathResource = new ClassPathResource("/script/my_py_zbar.py");
        try {
            pyZbarScriptPath = pathResource.getFile().getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping("/cameraScanPyZbar")
    public Object cameraScanPyZbar(@RequestBody String base64) throws IOException {
        if (this.checkBase64Head(base64)) {
            //1.去掉base64字元的頭部
            String base64Str = this.cutHead(base64);

            //2.建立臨時檔案(由於圖片的base64字元太長,不支援直接以命令引數的形式傳遞,故將字串寫入臨時檔案,而後python程式讀取臨時檔案內容)
            String tempPath = "./" + Thread.currentThread().getName();
            File tempFile = new File(tempPath);
            FileWriter fileWriter = new FileWriter(tempFile, false);
            fileWriter.write(base64Str);
            fileWriter.flush();
            fileWriter.close();

            //3.呼叫pyzbar解析base64字串
            String plainText = PyZbarUtil.executePyzbar("python", scriptPath, tempFile.getAbsolutePath());

            //4.刪除臨時檔案
            tempFile.delete();
            System.err.println("--------->" + plainText);
            return plainText;
        } else {
            return "引數格式錯誤";
        }
    }

    /**
     * 校驗Base64值是否已規定的串開始
     *
     * @param base64
     * @return
     */
    private boolean checkBase64Head(String base64) {
        return base64.startsWith(BASE_64_PREFIX);
    }

    /**
     * Base64去頭
     *
     * @param base64
     * @return
     */
    private String cutHead(String base64) {
        return base64.replaceAll(BASE_64_PREFIX, "");
    }
}

PyZbarUtil.java

package com.yang.springbootlucene.controller;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public final class PyZbarUtil {

    /**
     * 指令碼執行工具類
     *
     * @param lang       命令語言
     * @param scriptPath 指令碼絕對路勁
     * @param base64Path     base64檔案絕對路徑
     * @return
     */
    public static String executePyzbar(String lang, String scriptPath, String base64Path) {
        String[] arguments = new String[]{lang, scriptPath, base64Path};
        try {
            Process process = Runtime.getRuntime().exec(arguments);
            int re = process.waitFor();
            if (re == 0) {
                try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));) {
                    return in.readLine();
                }
            } else {
                System.err.println("指令碼呼叫失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
  1. python指令碼my_py_zbar.py
# -*-coding:UTF-8-*-

import sys
import base64
from io import BytesIO
import pyzbar.pyzbar as pyzbar
from PIL import Image,ImageEnhance


'''將base64轉換為位元組流'''
def convert_base64_to_byte_stream(base64_str):
    # 1.解碼Base64字串
    img_data = base64.b64decode(base64_str)

    # 2.將節碼結果轉為位元組流
    byte_stream = BytesIO(img_data)
    return byte_stream


'''從將位元組流解析二維碼'''
def parse_byte_stream_qr_code(byte_stream):
    # 3.開啟位元組流得到圖片物件
    img = Image.open(byte_stream)

    img = ImageEnhance.Brightness(img).enhance(2.0)  #增加亮度
    img = ImageEnhance.Contrast(img).enhance(4.0)  #增加對比度
    # img = ImageEnhance.Sharpness(img).enhance(17.0)  #銳利化
    # img = img.convert('L')  #灰度化

    # img.show()  # 播放圖片,供測試用

    # 4.呼叫pyzbar解析圖片中的二維碼
    barcodes = pyzbar.decode(img)

    # 5.列印解析結果
    return barcodes[0].data.decode("utf-8")


def main(argv):
#    print(parse_byte_stream_qr_code(convert_base64_to_byte_stream(argv[1])))
    print(parse_byte_stream_qr_code(convert_base64_to_byte_stream(open(argv[1]).readline())))


if __name__ == "__main__":
    main(sys.argv)
  1. yaml配置項
pyzbar:
  script: 'C:\Users\Tyrael\Desktop\pypy\script\my_py_zbar.py'
  1. 坑點:
  • 圖片轉base64後,得到的字串太長,不能直接以命令引數的形式傳遞,所以必須將其寫入臨時檔案,然後python指令碼讀取臨時檔案進行解析
  • 若將python指令碼放在專案中,專案打成jar包後,無法定位指令碼路徑,導致執行失敗,所以必須將指令碼放在jar包外,以配置的形式將路徑傳遞給java專案