|
|
@@ -0,0 +1,257 @@
|
|
|
+package com.tzld.piaoquan.growth.common.utils;
|
|
|
+
|
|
|
+import com.google.zxing.*;
|
|
|
+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;
|
|
|
+import okhttp3.Call;
|
|
|
+import okhttp3.OkHttpClient;
|
|
|
+import okhttp3.Request;
|
|
|
+import okhttp3.Response;
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.*;
|
|
|
+import java.nio.charset.Charset;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.Base64;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description: 生成二维码
|
|
|
+ * @author: TanJingyu
|
|
|
+ * @create:2023-06-12 11:17:01
|
|
|
+ **/
|
|
|
+public class QrCodes {
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(QrCodes.class);
|
|
|
+ /**
|
|
|
+ * 定义二维码图片的宽度
|
|
|
+ */
|
|
|
+ public static final int DEFAULT_WIDTH = 500;
|
|
|
+ /**
|
|
|
+ * 定义二维码图片的高度
|
|
|
+ */
|
|
|
+ public static final int DEFAULT_HEIGHT = 500;
|
|
|
+ /**
|
|
|
+ * 定义LOGO图片的宽度
|
|
|
+ */
|
|
|
+ public static final int DEFAULT_LOGO_WIDTH = 100;
|
|
|
+ /**
|
|
|
+ * 定义LOGO图片的高度
|
|
|
+ */
|
|
|
+ public static final int DEFAULT_LOGO_HEIGHT = 100;
|
|
|
+ /**
|
|
|
+ * 定义二维码的边距,默认一个单位
|
|
|
+ */
|
|
|
+ public static final int DEFAULT_MARGIN = 1;
|
|
|
+ /**
|
|
|
+ * 定义二维码生成后的预估大小,越准确的设置,越能避免byteArrayOutPutStream中数组的多次扩容
|
|
|
+ */
|
|
|
+ public static final int DEFAULT_ESTIMATE_SIZE = 9216;
|
|
|
+ /**
|
|
|
+ * 定义编码
|
|
|
+ */
|
|
|
+ public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
|
|
+ /**
|
|
|
+ * 定义二维码图像的后缀
|
|
|
+ */
|
|
|
+ public static final String DEFAULT_FORMAT = "png";
|
|
|
+
|
|
|
+
|
|
|
+ public static String createForBase64Str(String url) {
|
|
|
+ return createForBase64Str(url, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String createForBase64Str(String url, String logoLocation) {
|
|
|
+ return createForBase64Str(url, logoLocation, DEFAULT_FORMAT, DEFAULT_HEIGHT, DEFAULT_WIDTH, DEFAULT_LOGO_WIDTH, DEFAULT_LOGO_HEIGHT, DEFAULT_MARGIN, DEFAULT_ESTIMATE_SIZE, DEFAULT_CHARSET);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 前后端分离模式,推荐将二维码图片的二进制数组经base64成字符串返回给前端,这样有时可以避免前端再次请求后端
|
|
|
+ *
|
|
|
+ * @param url 二维码的真实地址
|
|
|
+ * @param logoLocation 二维码中间logo在后端项目中的位置,resource下边
|
|
|
+ * @param format 生成二维码的图片格式
|
|
|
+ * @param height 二维码图片的高度
|
|
|
+ * @param width 二维码图片的宽度
|
|
|
+ * @param logoWidth 中间logo宽度
|
|
|
+ * @param logoHeight 中间logo高度
|
|
|
+ * @param margin 二维码图片边距
|
|
|
+ * @param estimateSize 估计二维码图片的大小会是多大?越精准的估计,越能避免byteArrayOutPutStream中缓存数组的扩容
|
|
|
+ * @param charset 编码
|
|
|
+ */
|
|
|
+ public static String createForBase64Str(String url, String logoLocation, String format, int height, int width, int logoWidth, int logoHeight, int margin, int estimateSize, Charset charset) {
|
|
|
+ try {
|
|
|
+ /*生成二维码*/
|
|
|
+ // 定义Map集合封装二维码配置信息
|
|
|
+ Map<EncodeHintType, Object> hints = new HashMap<>();
|
|
|
+ // 设置二维码图片的内容编码
|
|
|
+ hints.put(EncodeHintType.CHARACTER_SET, charset.displayName());
|
|
|
+ // 设置二维码图片的上、下、左、右间隙
|
|
|
+ hints.put(EncodeHintType.MARGIN, margin);
|
|
|
+ // 设置二维码的纠错级别
|
|
|
+ hints.put(EncodeHintType.ERROR_CORRECTION,
|
|
|
+ ErrorCorrectionLevel.H);
|
|
|
+ //创建二维码字节转换对象
|
|
|
+ //第一个参数:二维码图片中的内容
|
|
|
+ //第二个参数:二维码格式器
|
|
|
+ //第三个参数:生成二维码图片的宽度
|
|
|
+ //第四个参数:生成二维码图片的高度
|
|
|
+ //第五个参数:生成二维码需要配置信息
|
|
|
+ BitMatrix matrix = new
|
|
|
+ MultiFormatWriter().encode(url,
|
|
|
+ BarcodeFormat.QR_CODE, width, height, hints);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /*生成放置二维码和中间logo的底图,并将二维码画上去*/
|
|
|
+ // 获取二维码图片真正的宽度
|
|
|
+ int matrix_width = matrix.getWidth();
|
|
|
+ // 获取二维码图片真正的高度
|
|
|
+ int matrix_height = matrix.getHeight();
|
|
|
+ // 定义一张空白的缓冲流图片
|
|
|
+ BufferedImage image = new
|
|
|
+ BufferedImage(matrix_width, matrix_height,
|
|
|
+ BufferedImage.TYPE_INT_RGB);
|
|
|
+ // 把二维码字节转换对象 转化 到缓冲流图片上
|
|
|
+ for (int x = 0; x < matrix_width; x++) {
|
|
|
+ for (int y = 0; y < matrix_height; y++) {
|
|
|
+ // 通过x、y坐标获取一点的颜色 true: 黑色 false: 白色
|
|
|
+ int rgb = matrix.get(x, y) ? 0x000000 : 0xFFFFFF;
|
|
|
+// int rgb = matrix.get(x, y) ? 0x008000 : 0xFFFFFF;
|
|
|
+ image.setRGB(x, y, rgb);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /*处理logo,把logo画到底图中间*/
|
|
|
+ InputStream logoStream = null;
|
|
|
+ if (StringUtils.isNotBlank(logoLocation)) {
|
|
|
+ logoStream = QrCodes.class.getClassLoader().getResourceAsStream(logoLocation);
|
|
|
+ if (Objects.isNull(logoStream)) {
|
|
|
+ logger.warn("生成二维码失败,未找到logo图");
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (Objects.nonNull(logoStream)) {
|
|
|
+ BufferedImage logo = ImageIO.read(logoStream);
|
|
|
+ // 获取缓冲流图片的画笔
|
|
|
+ Graphics2D g = (Graphics2D) image.getGraphics();
|
|
|
+ // 在二维码图片中间绘制公司logo
|
|
|
+ g.drawImage(logo, (matrix_width - logoWidth) / 2,
|
|
|
+ (matrix_height - logoHeight) / 2,
|
|
|
+ logoWidth, logoHeight, null);
|
|
|
+
|
|
|
+ // 设置画笔的颜色
|
|
|
+ g.setColor(Color.WHITE);
|
|
|
+ // 设置画笔的粗细
|
|
|
+ g.setStroke(new BasicStroke(5.0f));
|
|
|
+ // 设置消除锯齿
|
|
|
+ g.setRenderingHint(RenderingHints
|
|
|
+ .KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+ // 绘制圆角矩形
|
|
|
+ g.drawRoundRect((matrix_width - logoWidth) / 2,
|
|
|
+ (matrix_height - logoHeight) / 2,
|
|
|
+ logoWidth, logoHeight, 10, 10);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /*base64字符串返回图片*/
|
|
|
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(estimateSize);
|
|
|
+ ImageIO.write(image, format, byteArrayOutputStream);
|
|
|
+ return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("生成二维码出错: {}", e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String checkQRCode(String filePath){
|
|
|
+ long start = System.currentTimeMillis();
|
|
|
+ try {
|
|
|
+ // 读取二维码图片到缓冲区
|
|
|
+ BufferedImage image = ImageIO.read(new File(filePath));
|
|
|
+ LuminanceSource source = new BufferedImageLuminanceSource(image);
|
|
|
+ Binarizer binarizer = new HybridBinarizer(source);
|
|
|
+ BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
|
|
|
+ Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
|
|
|
+ hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
|
|
|
+ Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码
|
|
|
+ logger.info("checkQRCode ======>>encode Type: " + result.getBarcodeFormat() + " ======>>content:" + result.getText());
|
|
|
+ return result.getText();
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("checkQRCodeError" + e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String downloadAndCheckQRCode(String url, String filePath) {
|
|
|
+ try {
|
|
|
+ File image = new File(filePath);
|
|
|
+ downloadFile(url, image);
|
|
|
+ return QrCodes.checkQRCode(filePath);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("downloadAndCheckQRCode Error url:{} cost:{}", url, e);
|
|
|
+ } finally {
|
|
|
+ FileUtils.deleteFileByIO(filePath);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) throws IOException {
|
|
|
+ Image resultingImage = originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_AREA_AVERAGING);
|
|
|
+ BufferedImage outputImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
|
|
+ outputImage.getGraphics().drawImage(resultingImage, 0, 0, null);
|
|
|
+ return outputImage;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void downloadFile(String url, File file) throws Exception {
|
|
|
+ Request request = new Request.Builder()
|
|
|
+ .url(url)
|
|
|
+ .build();
|
|
|
+ OkHttpClient client = new OkHttpClient().newBuilder()
|
|
|
+ .connectTimeout(3, TimeUnit.MINUTES)
|
|
|
+ .readTimeout(1, TimeUnit.MINUTES)
|
|
|
+ .writeTimeout(1, TimeUnit.MINUTES).build();
|
|
|
+ Call call = client.newCall(request);
|
|
|
+ Response response = call.execute();
|
|
|
+ InputStream is = response.body().byteStream();
|
|
|
+ FileOutputStream outputStream = new FileOutputStream(file);
|
|
|
+ byte[] buffer = new byte[4096];
|
|
|
+ int bytesRead;
|
|
|
+ while ((bytesRead = is.read(buffer)) != -1) {
|
|
|
+ outputStream.write(buffer, 0, bytesRead);
|
|
|
+ }
|
|
|
+ is.close();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ String filePath = "/Users/wangyunpeng/IdeaProjects/aigc-admin/datalog/weblog/aigc-admin/";
|
|
|
+ String fileName = BaseUtils.getUUIDStr();
|
|
|
+ try {
|
|
|
+ File image = new File(filePath + fileName);
|
|
|
+ downloadFile("https://mmbiz.qpic.cn/sz_mmbiz_png/rGQMLMianRiaZgicELeh23DhLhjTTY2BPRqp3OwGDtXIAp7Za75iazicK29p2LKfnjdZVRsgyTkXASdttsINd9erkew/640?wx_fmt=png&from=appmsg", image);
|
|
|
+ System.out.println(QrCodes.checkQRCode(filePath + fileName));
|
|
|
+ } catch (Exception e) {
|
|
|
+
|
|
|
+ } finally {
|
|
|
+// FileUtils.deleteFileByIO(filePath + fileName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|