From b6df9a1056a450c6a429027664e9d88cd971330e Mon Sep 17 00:00:00 2001 From: "382696293@qq.com" <382696293@qq.com> Date: Sat, 6 Jan 2024 14:24:53 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E6=88=90=E4=BA=8C=E7=B6=AD=E7=A2=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/controller/QrCodeController.java | 33 +++ .../com/flossom/system/utils/QrCodeUtil.java | 213 ++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 flossom-modules/flossom-system/src/main/java/com/flossom/system/controller/QrCodeController.java create mode 100644 flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/QrCodeUtil.java diff --git a/flossom-modules/flossom-system/src/main/java/com/flossom/system/controller/QrCodeController.java b/flossom-modules/flossom-system/src/main/java/com/flossom/system/controller/QrCodeController.java new file mode 100644 index 0000000..4e6dcd8 --- /dev/null +++ b/flossom-modules/flossom-system/src/main/java/com/flossom/system/controller/QrCodeController.java @@ -0,0 +1,33 @@ +package com.flossom.system.controller; + +import com.flossom.common.core.exception.ServiceException; +import com.flossom.system.utils.QrCodeUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@RestController +@RequestMapping("/qrcode") +public class QrCodeController { + + @Value("${qrcode.url}") + private String qrcodeUrl; + + @GetMapping("create") + public void create(HttpServletResponse response, @Param("serial") String serial) throws IOException { + if (StringUtils.isBlank(serial)) { + throw new ServiceException("请输入序列号"); + } + if (StringUtils.isBlank(qrcodeUrl)) { + throw new ServiceException("请配置二維碼地址"); + } + QrCodeUtil.buildQrCodeImage(qrcodeUrl + serial, serial, response.getOutputStream()); + } + +} diff --git a/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/QrCodeUtil.java b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/QrCodeUtil.java new file mode 100644 index 0000000..ecb0073 --- /dev/null +++ b/flossom-modules/flossom-system/src/main/java/com/flossom/system/utils/QrCodeUtil.java @@ -0,0 +1,213 @@ +package com.flossom.system.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.QRCodeReader; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Hashtable; + +/** + * 生成二维码工具类. + */ +@Component +public class QrCodeUtil { + //二维码宽度 + private static final int QRCODE_SIZE = 430; + //编码 + private static final String CHARSET = "utf-8"; + // 二维码绘制高度偏移量,留出空间写文字描述二维码信息 + private static final int OFFSET_HEIGHT = 25; + //二维码标题字体 + private static final String TITLE_FONT = "黑体"; + // logo大小 + private static final int LOGO_PART = 4; + // + private static String FORMAT = "PNG"; + //标题前缀 + private static final String TITLE_PREFIX = "花至微信小程序序列码:"; + + public static BufferedImage buildQrCodeImage(String content) { + return createImage(content, null, null); + } + + public static BufferedImage buildQrCodeImage(String content, String title) { + return createImage(content, title, null); + } + + public static BufferedImage buildQrCodeImage(String content, String title, String logoPath) { + return createImage(content, title, logoPath); + } + + public static void buildQrCodeImage(String content, OutputStream outputStream) { + BufferedImage image = createImage(content, null, null); + try { + ImageIO.write(image, FORMAT, outputStream); + } catch (IOException e) { + throw new RuntimeException("生成二维码信息异常,请稍后重试"); + } + } + + public static void buildQrCodeImage(String content, String title, OutputStream outputStream) { + BufferedImage image = createImage(content, title, null); + try { + ImageIO.write(image, FORMAT, outputStream); + } catch (IOException e) { + throw new RuntimeException("生成二维码信息异常,请稍后重试"); + } + } + + public static void buildQrCodeImage(String content, String title, String logoPath, OutputStream outputStream) { + BufferedImage image = createImage(content, title, logoPath); + try { + ImageIO.write(image, FORMAT, outputStream); + } catch (IOException e) { + throw new RuntimeException("生成二维码信息异常,请稍后重试"); + } + } + + public static void buildQrCodeImage(String content, String title, String logoPath, String outputPath) { + BufferedImage image = createImage(content, title, logoPath); + try { + FileOutputStream outputStream = new FileOutputStream(new File(outputPath)); + ImageIO.write(image, FORMAT, outputStream); + } catch (IOException e) { + throw new RuntimeException("生成二维码信息异常,请稍后重试"); + } + } + + public static Result analysisQrCode(BufferedImage image) { + QRCodeReader qrCodeReader = new QRCodeReader(); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image))); + try { + return qrCodeReader.decode(bitmap); + } catch (Exception e) { + throw new RuntimeException("解析二维码信息失败"); + } + } + + public static Result analysisQrCode(String QrCodePath) { + QRCodeReader qrCodeReader = new QRCodeReader(); + BufferedImage img = null; + try { + img = ImageIO.read(new File(QrCodePath)); + } catch (IOException e) { + throw new RuntimeException("读取二维码失败"); + } + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(img))); + try { + return qrCodeReader.decode(bitmap); + } catch (Exception e) { + throw new RuntimeException("解析二维码失败"); + } + } + + + /** + * 生成二维码. + * + * @param content 扫描成功的内容 + * @param title 二维码标题 + * @return 二维码图片 + */ + private static BufferedImage createImage(String content, String title, String logoPath) { + //等同于hashmap,hashtable是线程安全的 + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + hints.put(EncodeHintType.CHARACTER_SET, CHARSET); + hints.put(EncodeHintType.MARGIN, 1); + BitMatrix bitMatrix = null; + try { + bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints); + } catch (WriterException e) { + throw new RuntimeException("生成二维码信息异常,请稍后重试"); + } + 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); + } + } + + // 如果设置了二维码里面的logo 加入LOGO水印 + if (!StringUtils.isEmpty(logoPath)) { + try { + image = drawLogoForQR(image, logoPath); + } catch (IOException e) { + throw new RuntimeException("生成二维码信息异常,请稍后重试"); + } + } + //有标题,合成带标题的二维码 + if (StringUtils.isNotBlank(title)) { + return drawTitleForQR(image, title); + } + //直接返回生成的二维码 + return image; + } + + + /** + * 绘制二维码描述信息. + * + * @param source 源二维码图片 + * @param title 二维码标题 + * @return 合成后的图片 + */ + private static BufferedImage drawTitleForQR(BufferedImage source, String title) { + //新建模板图片 + BufferedImage bufferedImage = new BufferedImage(QRCODE_SIZE, QRCODE_SIZE + OFFSET_HEIGHT, BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = bufferedImage.createGraphics(); + //绘制矩形背景 + graphics.setColor(Color.white); + graphics.fillRect(0, 0, QRCODE_SIZE, OFFSET_HEIGHT); + //绘制描述信息 + Font font = new Font(TITLE_FONT, Font.PLAIN, 22); + graphics.setColor(Color.black); + graphics.setFont(font); + graphics.drawString(TITLE_PREFIX + title, 20, OFFSET_HEIGHT - 2); + //绘制二维码 + graphics.drawImage(source, 0, OFFSET_HEIGHT, QRCODE_SIZE, QRCODE_SIZE, null); + graphics.dispose(); + return bufferedImage; + } + + /** + * 给二维码图片中绘制logo信息 非必须 + * + * @param image 二维码图片 + * @param logoPath logo图片路径 + */ + private static BufferedImage drawLogoForQR(BufferedImage image, String logoPath) throws IOException { + Graphics2D g = image.createGraphics(); + BufferedImage logoImage = ImageIO.read(new File(logoPath)); + // 计算logo图片大小,可适应长方形图片,根据较短边生成正方形 + int width = image.getWidth() < image.getHeight() ? image.getWidth() / LOGO_PART : image.getHeight() / LOGO_PART; + int height = width; + // 计算logo图片放置位置 + int x = (image.getWidth() - width) / 2; + int y = (image.getHeight() - height) / 2; + // 在二维码图片上绘制中间的logo + g.drawImage(logoImage, x, y, width, height, null); + // 绘制logo边框,可选 + g.setStroke(new BasicStroke(2)); // 画笔粗细 + g.setColor(Color.WHITE); // 边框颜色 + g.drawRect(x, y, width, height); // 矩形边框 + logoImage.flush(); + g.dispose(); + return image; + } +} +