本篇文章将介绍Java使用Zxing和commons-codec实现生成二维码图片和二维码Base64。
说明:针对Maven工程和普通的java工程,jar包的引入方式不同,此处只介绍Maven工程jar引入方式。
com.google.zxing
core
3.4.0
com.google.zxing
javase
3.4.0
commons-codec
commons-codec
1.15
建议:jar有时候不一定非要引入最新的,在maven仓库中可以在最近的年份中选择使用人数最多的。
package utils;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import org.apache.commons.codec.binary.Base64;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
/**
* @name QrCodeUtils
* @Description URL转Base64二维码
* @author wanglong
* @date 2021年1月20日下午1:08:53
* @version 1.0
*/
public class QrCodeUtils {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static String creatRrCode(String contents, int width, int height) {
String base64 = "";
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
try {
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints);
// 1、读取文件转换为字节数组
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedImage image = toBufferedImage(bitMatrix);
//转换成png格式的IO流
ImageIO.write(image, "png", out);
byte[] bytes = out.toByteArray();
// 2、将字节数组转为二进制
base64 = Base64.encodeBase64String(bytes).trim();
} catch (WriterException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return base64;
}
/**
* image流数据处理
*/
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) ? 0xFF000000 : 0xFFFFFFFF);
}
}
return image;
}
public static void main(String[] args) {
// 测试代码
String base64Pic = QrCodeUtils.creatRrCode("http://zf.thxyy.cn/weixinmpPlus/byCodePay/list?dd=JC2101080005&ts=1610080940", 200,200);
System.out.println(base64Pic);
}
}
说明:此处生成Base64,使用的是org.apache.commons.codec.binary.Base64,而不是sun.misc.BASE64Encoder,因为后者在有的ide中提示是内部方法,有可能在未来版本会删除。建议还是使用第三方开源的。
使用场景:
<1>返回给前台,直接展示: // 记得从后台返回的base64字符串要加上前缀:data:image/jpeg;base64,
<2>在jasper中使用,打印pdf预览(下面只提供简单样例,里面用到的工具类不在这书写):
// 4、打印 记录 @Override public Map
selectCourseGeneralData(String id, String token) { Map map = new HashMap<>(); List 下面来说下在jasper中的配置(此图与上面样例有些出入,重点是看图中的base64Pic所在位置)
base64Pic字段所在的Image控件配置如下图所示:
在Image Expression属性中,对base64Pic要做处理: new ByteArrayInputStream(org.apache.commons.codec.binary.Base64.decodeBase64($P{base64Pic}.getBytes()))
至此,对于URL转换为Base64二维码及展示大体如上所示。
package utils; import java.awt.BasicStroke; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Shape; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.OutputStream; import java.util.Hashtable; import java.util.Random; import javax.imageio.ImageIO; import com.google.zxing.BarcodeFormat; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatWriter; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; public class QrCodeUtils2 { private static final String CHARSET = "utf-8"; private static final String FORMAT = "JPG"; // 二维码尺寸 private static final int QRCODE_SIZE = 300; // LOGO宽度 private static final int LOGO_WIDTH = 60; // LOGO高度 private static final int LOGO_HEIGHT = 60; /** * 公用方法:1、生成图片 */ private static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception { Hashtable
hints = new Hashtable (); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.CHARACTER_SET, CHARSET); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints); int width = bitMatrix.getWidth(); int height = bitMatrix.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, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); } } if (logoPath == null || "".equals(logoPath)) { return image; } // 插入图片 QrCodeUtils2.insertImage(image, logoPath, needCompress); return image; } /** * 公用方法:2、插入LOGO * @param source 二维码图片 * @param logoPath LOGO图片地址 * @param needCompress 是否压缩 */ private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws Exception { File file = new File(logoPath); if (!file.exists()) { throw new Exception("logo file not found."); } Image src = ImageIO.read(new File(logoPath)); int width = src.getWidth(null); int height = src.getHeight(null); if (needCompress) { // 压缩LOGO if (width > LOGO_WIDTH) { width = LOGO_WIDTH; } if (height > LOGO_HEIGHT) { height = LOGO_HEIGHT; } Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(image, 0, 0, null); // 绘制缩小后的图 g.dispose(); src = image; } // 插入LOGO Graphics2D graph = source.createGraphics(); int x = (QRCODE_SIZE - width) / 2; int y = (QRCODE_SIZE - height) / 2; graph.drawImage(src, x, y, width, height, null); Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6); graph.setStroke(new BasicStroke(3f)); graph.draw(shape); graph.dispose(); } /** * 1、生成二维码(内嵌LOGO): 二维码文件名随机,文件名可能会有重复 * @param content 内容 * @param logoPath LOGO地址 * @param destPath 存放目录 * @param needCompress 是否压缩LOGO */ public static String encode(String content, String logoPath, String destPath, boolean needCompress) throws Exception { BufferedImage image = QrCodeUtils2.createImage(content, logoPath, needCompress); mkdirs(destPath); String fileName = new Random().nextInt(99999999) + "." + FORMAT.toLowerCase(); ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName)); return fileName; } /** * 1.5 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir。 (mkdir如果父目录不存在则会抛出异常) * @param destPath 存放目录 */ public static void mkdirs(String destPath) { File file = new File(destPath); if (!file.exists() && !file.isDirectory()) { file.mkdirs(); } } /** * 2、生成二维码(内嵌LOGO):调用指定二维码文件名 * @param content 内容 * @param logoPath LOGO地址 * @param destPath 存放目录 * @param fileName 二维码文件名 * @param needCompress 是否压缩LOGO */ public static String encode(String content, String logoPath, String destPath, String fileName, boolean needCompress) throws Exception { BufferedImage image = QrCodeUtils2.createImage(content, logoPath, needCompress); mkdirs(destPath); fileName = fileName.substring(0, fileName.indexOf(".") > 0 ? fileName.indexOf(".") : fileName.length()) + "." + FORMAT.toLowerCase(); ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName)); return fileName; } /** * 3、生成二维码(内嵌LOGO) * @param content 内容 * @param logoPath LOGO地址 * @param destPath 存储地址 */ public static String encode(String content, String logoPath, String destPath) throws Exception { return QrCodeUtils2.encode(content, logoPath, destPath, false); } /** * 4、生成二维码 * @param content 内容 * @param destPath 存储地址 * @param needCompress 是否压缩LOGO */ public static String encode(String content, String destPath, boolean needCompress) throws Exception { return QrCodeUtils2.encode(content, null, destPath, needCompress); } /** * 5、生成二维码 * @param content 内容 * @param destPath 存储地址 */ public static String encode(String content, String destPath) throws Exception { return QrCodeUtils2.encode(content, null, destPath, false); } /** * 6、生成二维码(内嵌LOGO) * @param content 内容 * @param logoPath LOGO地址 * @param output 输出流 * @param needCompress 是否压缩LOGO */ public static void encode(String content, String logoPath, OutputStream output, boolean needCompress) throws Exception { BufferedImage image = QrCodeUtils2.createImage(content, logoPath, needCompress); ImageIO.write(image, FORMAT, output); } /** * 7、生成二维码 * @param content 内容 * @param output 输出流 */ public static void encode(String content, OutputStream output) throws Exception { QrCodeUtils2.encode(content, null, output, false); } /** * 8、解析二维码 * @param file 二维码图片 */ public static String decode(File file) throws Exception { BufferedImage image; image = ImageIO.read(file); if (image == null) { return null; } BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Result result; Hashtable hints = new Hashtable (); hints.put(DecodeHintType.CHARACTER_SET, CHARSET); result = new MultiFormatReader().decode(bitmap, hints); String resultStr = result.getText(); return resultStr; } /** * 9、解析二维码 * @param path 二维码图片地址 */ public static String decode(String path) throws Exception { return QrCodeUtils2.decode(new File(path)); } public static void main(String[] args) throws Exception { String text = "http://zf.thxyy.cn/weixinmpPlus/byCodePay/list?dd=JC2101080005&ts=1610080940"; //不含Logo QrCodeUtils2.encode(text, null, "C:\\jasper", true); //含Logo,不指定二维码图片名 QrCodeUtils2.encode(text, "E:\\WallPaper\\o_hd2.jpg", "C:\\jasper", true); //含Logo,指定二维码图片名 QrCodeUtils2.encode(text, "E:\\WallPaper\\o_hd2.jpg", "C:\\jasper", "qrcode", true); } }