|
@@ -0,0 +1,214 @@
|
|
|
+package com.bofeng.unit;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.toolkit.IdWorker;
|
|
|
+import com.google.common.base.Strings;
|
|
|
+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;
|
|
|
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
|
|
+import org.apache.commons.codec.binary.Base64;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.File;
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Paths;
|
|
|
+import java.nio.file.StandardOpenOption;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+public class QRCode {
|
|
|
+
|
|
|
+ private static String programUploadPath = "D:\\uoplad\\whepi";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * CODE_WIDTH:二维码宽度,单位像素
|
|
|
+ * CODE_HEIGHT:二维码高度,单位像素
|
|
|
+ * FRONT_COLOR:二维码前景色,0x000000 表示黑色
|
|
|
+ * BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色
|
|
|
+ * 演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白
|
|
|
+ */
|
|
|
+ private static final int CODE_WIDTH = 256;
|
|
|
+ private static final int CODE_HEIGHT = 256;
|
|
|
+ private static final int FRONT_COLOR = 0x000000;
|
|
|
+ private static final int BACKGROUND_COLOR = 0xFFFFFF;
|
|
|
+
|
|
|
+ public static BufferedImage createQRCode(String content, BufferedImage bufferedImage, int topFontWidth, int topFontHeight, int bottomFontWidth, int bottomFontHeight) throws WriterException {
|
|
|
+ //检验参数
|
|
|
+ if (content == null || "".equals(content)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (bufferedImage == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ content = content.trim();
|
|
|
+ int width = bufferedImage.getWidth();
|
|
|
+ int height = bufferedImage.getHeight();
|
|
|
+ int startBottomY = height + topFontHeight;
|
|
|
+ height = startBottomY + bottomFontHeight;
|
|
|
+ /**com.google.zxing.EncodeHintType:编码提示类型,枚举类型
|
|
|
+ * EncodeHintType.CHARACTER_SET:设置字符编码类型
|
|
|
+ * EncodeHintType.ERROR_CORRECTION:设置误差校正
|
|
|
+ * ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction
|
|
|
+ * 不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的
|
|
|
+ * EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近
|
|
|
+ * */
|
|
|
+ Map<EncodeHintType, Object> hints = new HashMap();
|
|
|
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
|
|
|
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.Q);
|
|
|
+ hints.put(EncodeHintType.MARGIN, 1);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * MultiFormatWriter:多格式写入,这是一个工厂类,里面重载了两个 encode 方法,用于写入条形码或二维码
|
|
|
+ * encode(String contents,BarcodeFormat format,int width, int height,Map<EncodeHintType,?> hints)
|
|
|
+ * contents:条形码/二维码内容
|
|
|
+ * format:编码类型,如 条形码,二维码 等
|
|
|
+ * width:码的宽度
|
|
|
+ * height:码的高度
|
|
|
+ * hints:码内容的编码类型
|
|
|
+ * BarcodeFormat:枚举该程序包已知的条形码格式,即创建何种码,如 1 维的条形码,2 维的二维码 等
|
|
|
+ * BitMatrix:位(比特)矩阵或叫2D矩阵,也就是需要的二维码
|
|
|
+ */
|
|
|
+ MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
|
|
|
+ BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
|
|
|
+
|
|
|
+ /**java.awt.image.BufferedImage:具有图像数据的可访问缓冲图像,实现了 RenderedImage 接口
|
|
|
+ * BitMatrix 的 get(int x, int y) 获取比特矩阵内容,指定位置有值,则返回true,将其设置为前景色,否则设置为背景色
|
|
|
+ * BufferedImage 的 setRGB(int x, int y, int rgb) 方法设置图像像素
|
|
|
+ * x:像素位置的横坐标,即列
|
|
|
+ * y:像素位置的纵坐标,即行
|
|
|
+ * rgb:像素的值,采用 16 进制,如 0xFFFFFF 白色
|
|
|
+ */
|
|
|
+ BufferedImage newBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
|
|
|
+ for (int x = 0; x < width; x++) {
|
|
|
+ for (int y = 0; y < height; y++) {
|
|
|
+ //顶部文字填充区域设置为空白
|
|
|
+ if (y <= topFontHeight) {
|
|
|
+ newBufferedImage.setRGB(x, y, BACKGROUND_COLOR);
|
|
|
+ }
|
|
|
+ //底部文字填充区域设置为空白
|
|
|
+ else if (y > startBottomY) {
|
|
|
+ newBufferedImage.setRGB(x, y, BACKGROUND_COLOR);
|
|
|
+ } else {
|
|
|
+ newBufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ bufferedImage = null; //主动释放
|
|
|
+ return newBufferedImage;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String createBase64Img(String content, String topFont, String bottomFont) throws WriterException, IOException {
|
|
|
+ BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_RGB);
|
|
|
+ if (content == null || "".equals(content)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ topFont = topFont == null ? "" : topFont;
|
|
|
+ bottomFont = bottomFont == null ? "" : bottomFont;
|
|
|
+ bufferedImage = pressTextInImage(bufferedImage, content, topFont, bottomFont);
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
+ //写入流中
|
|
|
+ ImageIO.write(bufferedImage, "jpg", outputStream);
|
|
|
+ //转换成字节
|
|
|
+ byte[] bytes = outputStream.toByteArray();
|
|
|
+ //对字节数组Base64编码 -- 转换成base64串
|
|
|
+ /* BASE64Encoder encoder = new BASE64Encoder();
|
|
|
+ String base64Img = encoder.encodeBuffer(bytes).trim();*/
|
|
|
+ Base64 encoder = new Base64();
|
|
|
+ String base64Img = encoder.encodeToString(bytes);
|
|
|
+
|
|
|
+ //删除 \r\n
|
|
|
+ base64Img = base64Img.replaceAll("\n", "").replaceAll("\r", "");
|
|
|
+ //前面加 data:image/jpg;base64,
|
|
|
+ String res = "data:image/jpg;base64," + base64Img.toString();
|
|
|
+ //关闭流
|
|
|
+ if (outputStream != null) {
|
|
|
+ outputStream.close();
|
|
|
+ }
|
|
|
+// System.out.println(res);
|
|
|
+ return decryptByBase64(res);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static BufferedImage pressTextInImage(BufferedImage bufferedImage, String content, String topFont, String bottomFont) throws WriterException {
|
|
|
+ if (bufferedImage == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ //图片绘制对象 Color.RED
|
|
|
+ Graphics graphics = bufferedImage.createGraphics();
|
|
|
+ Font font = new Font("黑体", 5, 18);
|
|
|
+ FontMetrics metrics = graphics.getFontMetrics(font);
|
|
|
+ //获取底部字体的宽和高
|
|
|
+ int bottomFontWidth = metrics.stringWidth(bottomFont);
|
|
|
+ int bottomFontHeight = metrics.getHeight();
|
|
|
+ //获取顶部字体的宽和高
|
|
|
+ int topFontWidth = metrics.stringWidth(topFont);
|
|
|
+ int topFontHeight = metrics.getHeight();
|
|
|
+ //将image生成二维码图片对象
|
|
|
+ bufferedImage = createQRCode(content, bufferedImage, topFontWidth, topFontHeight, bottomFontWidth, bottomFontHeight);
|
|
|
+ //获取二维码图片的宽和高
|
|
|
+ int imageW = bufferedImage.getWidth();
|
|
|
+ int imageH = bufferedImage.getHeight();
|
|
|
+ //计算顶部文字填充位置
|
|
|
+ int topStartX = (imageW - topFontWidth) / 2; //居中显示
|
|
|
+ int topStartY = topFontHeight;
|
|
|
+ //计算底部文字填充位置
|
|
|
+ int bottomStartX = (imageW - bottomFontWidth) / 2; //居中显示
|
|
|
+ int bottomStartY = (imageH - bottomFontHeight) + 2;
|
|
|
+
|
|
|
+ //文字图片对象
|
|
|
+ BufferedImage textImag = new BufferedImage(imageW, imageH, BufferedImage.TYPE_INT_RGB);
|
|
|
+ Graphics textGraphics = textImag.createGraphics();
|
|
|
+ //画图
|
|
|
+ textGraphics.drawImage(bufferedImage, 0, 0, imageW, imageH, null);
|
|
|
+ // 写底部文字
|
|
|
+ textGraphics.setColor(Color.RED);
|
|
|
+ textGraphics.setFont(font);
|
|
|
+ textGraphics.drawString(bottomFont, bottomStartX, bottomStartY);
|
|
|
+ //写顶部字体
|
|
|
+ textGraphics.setColor(Color.RED);
|
|
|
+ textGraphics.setFont(font);
|
|
|
+ textGraphics.drawString(topFont, topStartX, topStartY);
|
|
|
+ graphics.dispose();
|
|
|
+ return textImag;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * base64转存文件
|
|
|
+ * @param base64
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public static String decryptByBase64(String base64) {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd");
|
|
|
+ if (Strings.isNullOrEmpty(base64)) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ String filePath = programUploadPath + sdf.format(new Date()) + "/" + IdWorker.getId() + ".jpg";
|
|
|
+ File path = new File(programUploadPath + sdf.format(new Date()));
|
|
|
+ if (!path.exists()) {
|
|
|
+ path.mkdirs();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Files.write(Paths.get(filePath),
|
|
|
+ Base64.decodeBase64(base64.substring(base64.indexOf(",") + 1)), StandardOpenOption.CREATE);
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return filePath.replace(programUploadPath, "");
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) throws IOException, WriterException {
|
|
|
+ Long startTime = System.currentTimeMillis();
|
|
|
+ System.out.println("******************************");
|
|
|
+ String file = createBase64Img("https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx02a4ee296708dfd4&redirect_uri=http%3A%2F%2Fpeiguo.ng.yvanui.com%2Fuser%2FscanEstate.html&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect", "家园互助平台", "巡检扫码");
|
|
|
+ Long endTime = System.currentTimeMillis();
|
|
|
+ System.out.println(file);
|
|
|
+ System.out.println((endTime - startTime) + " |" +(endTime - startTime)/ 1000);
|
|
|
+ }
|
|
|
+}
|