针对特殊需求需要使用二维码传输数据,为了降低二维码的复杂度和提高数据传输量,需要先对数据进行压缩,然后生成二维码。压缩后的数据是byte[],如果再转回字符串会严重影响压缩效果,因此考虑直接使用byte[]生成和解析二维码。为了实现使用byte[]生成二维码,对zxing的core-3.4.0进行了改写。
改写步骤:zxing使用byte数组生成二维码和解析二维码。
改写的jar:重新编译的可使用byte[]生成二维码的zxing-core-3.4.0
使用时需要导入改写的core-3.4.0.jar和javase-3.4.0.jar。压缩使用jdk8自带的GZIP对字符串进行压缩,测试了几次压缩率大约在50%~60%之间,测试的次数不多可能不是太准确。详细代码如下,具体使用参照main方法。
private static final Charset UTF8 = Charset.forName("UTF-8");//编码格式
private static final int MARGIN = 10;//二维码边距
private static final int WIDTH = 800;//二维码宽
private static final int HEIGHT = 800;//二维码高
private static final String SUFFIX = "jpg";//图片后缀名
private static final String QRPATH = "/qrcode.jpg";//二维码生成路径
/**
* 压缩字符串为数组
* @param str
* @return
*/
public static byte[] compress(String str) {
if (str == null || str.length() == 0) {
return null;
}
ByteArrayOutputStream out= new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(out);){
gzip.write(str.getBytes(UTF8));
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
/**
* 数组解压缩为字符串
* @param bytes
* @return
*/
public static String uncompress(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try (GZIPInputStream gzip = new GZIPInputStream(in);){
byte[] buffer = new byte[1024];
int n;
while ((n = gzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
return new String(out.toByteArray(),SysUtils.UTF8);
}
/**
*指定位置生成二维码
* @param data 二维码数据
* @return
*/
public static String encodeQRCode(byte[] data) {
File file = new File(QRPATH);
encodeQRCode(data, ErrorCorrectionLevel.M, MARGIN, WIDTH, HEIGHT, SUFFIX, file.toPath());
return QRPATH;
}
/**
* 生成二维码
* @param data 二维码数据
* @param level 纠错等级
* @param margin 二维码边距
* @param width 二维码宽
* @param height 二维码高
* @param suffix 二维码图片后缀名
* @param path 二维码图片路径
*/
public static void encodeQRCode(byte[] data, ErrorCorrectionLevel level, int margin, int width, int height, String suffix, Path path) {
// 定义二维码的参数
HashMap<EncodeHintType, Object> hashMap = new HashMap<EncodeHintType, Object>();
// 设置二维码纠错等级
hashMap.put(EncodeHintType.ERROR_CORRECTION, level);
// 设置二维码边距
hashMap.put(EncodeHintType.MARGIN, margin);
try {
// 开始生成二维码
BitMatrix bitMatrix = new MultiFormatWriter().encodeQRCode(data, width, height, hashMap);
// 导出到指定目录
MatrixToImageWriter.writeToPath(bitMatrix, suffix, path);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 识别二维码
* @param file
* @return
*/
public static Result decodeQRCode(File file) {
try {
return decodeQRCode(ImageIO.read(file));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 识别二维码
* @param image
* @return
*/
public static Result decodeQRCode(BufferedImage image) {
LuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, UTF8);
try {
return new MultiFormatReader().decodeQRCode(bitmap, hints);
} catch (NotFoundException e) {
e.printStackTrace();
}
return null;
}
public static void main(String...strings) {
String data = "针对特殊需求需要使用二维码传输数据,为了降低二维码的复杂度和提高数据传输量,需要先对数据进行压缩,然后生成二维码。压缩后的数据是byte[],如果再转回字符串会严重影响压缩效果,因此考虑直接使用byte[]生成和解析二维码。为了实现使用byte[]生成二维码,对zxing的core-3.4.0进行了改写";
encodeQRCode(compress(data));
System.out.println(uncompress(decodeQRCode(new File(QRPATH)).getRawBytes()));
}
compress(String str):压缩字符串为byte数组,编码格式为UTF-8,可根据需要修改配置。
uncompress(byte[] bytes):解压缩byte数组为字符串,编码格式为UTF-8,可根据需要修改配置。
encodeQRCode(byte[] data):使用byte数组生成二维码,可根据需要修改宽、高等配置。
encodeQRCode(byte[] data, ErrorCorrectionLevel level, int margin, int width, int height, String suffix, Path path) :使用byte数组生成二维码,可自定义二维码宽、高、纠错等级等。
decodeQRCode(File file):识别二维码图片,解析二维码得到Result对象,使用Result对象的getText()方法得到字符串,压缩字符串生成的二维码不可使用,应使用getRawBytes()方法得到byte数组进行解压缩。
decodeQRCode(BufferedImage image):同decodeQRCode(File file),但是可以扩展解析base64二维图片,代码如下:
/**
* 识别二维码
* @param base64Img
* @return
*/
public static Result decodeQRCode(String base64Img) {
if(base64Img.startsWith("data:image")) {
base64Img = base64Img.split(",")[1];
byte[] buf = Base64.getDecoder().decode(base64Img);
InputStream input = new ByteArrayInputStream(buf);
BufferedImage image;
try {
image = ImageIO.read(input);
return decodeQRCode(image);
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}