• java生成、识别条形码和二维码


    一、概述

    • 使用 zxing 开源库

      • Zxing主要是Google出品的,用于识别一维码和二维码的第三方库
      • 主要类:
      • BitMatrix 位图矩阵
      • MultiFormatWriter 位图编写器
      • MatrixToImageWriter 写入图片
    • 可以生成、识别条形码和二维码

    • 内置三种尺寸:enum Size {SMALL, MIDDLE, BIG}

    • 依赖

    <dependency>
        <groupId>com.google.zxinggroupId>
        <artifactId>javaseartifactId>
        <version>3.5.1version>
    dependency>
    <dependency>
        <groupId>com.google.zxinggroupId>
        <artifactId>coreartifactId>
        <version>3.5.1version>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
        <version>2.6.7version>
    dependency>
    <dependency>
        <groupId>commons-langgroupId>
        <artifactId>commons-langartifactId>
        <version>2.6version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    二、效果图

    三、条形码

    • 将宽度不等的多个黑条和白条,按照一定的编码规则排序,用以表达一组信息的图像标识符
    • 通常代表一串数字 / 字母,每一位有特殊含义
    • 一般数据容量30个数字 / 字母

    1、生成简单条形码(无文字)

    /**
     * 生成简单条形码(无文字)
     *
     * @param content
     * @param width
     * @param height
     * @return
    */
    public static BufferedImage create(String content, int width, int height) {
        // 定义位图矩阵BitMatrix
        BitMatrix matrix = null;
    
        try {
            // 定义二维码参数
            Map<EncodeHintType, Object> hints = new HashMap<>(3);
            // 设置编码
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            // 设置容错等级
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            // 设置边距,默认为5
            hints.put(EncodeHintType.MARGIN, 3);
    
            // 使用code_128格式进行编码生成条形码
            MultiFormatWriter writer = new MultiFormatWriter();
            matrix = writer.encode(content, BarcodeFormat.CODE_128, width, height, hints);
        } catch (WriterException e) {
            e.printStackTrace();
            //throw new RuntimeException("条形码内容写入失败");
        }
    
        return MatrixToImageWriter.toBufferedImage(matrix);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2、生成条形码(含文字)

    • 生成条形码(含文字)
    /**
     * 生成条形码(含文字)
     * ****************************************************
     * ----------------------------------------------
     * |                         2023-06-10 10:55   |
     * |                                            |
     * |            商品名称 /超出不显示/              |
     * |                                            |
     * |    | || ||| | || |||| | | | ||| | | ||     |
     * |    | || ||| | || |||| | | | ||| | | ||     |
     * |    | || ||| | || |||| | | | ||| | | ||     |
     * |    | || ||| | || |||| | | | ||| | | ||     |
     * |               13800000000                  |
     * ----------------------------------------------
     * ===================================================
     * 1、日期:顶部右侧
     * 2、商品名称:居中
     * 3、条形码:商品名称下方,且居中
     * 4、条码内容:条形码下方,且居中
     * *****************************************************
     *
     * @param codeImage     条形码图片
     * @param bottomStr     底部文字
     * @param titleStr      标题文字
     * @param topRightStr   右上角文字
     * @param pictureWidth  图片宽度
     * @param pictureHeight 图片高度
     * @param margin        边距
     * @param fontSize      字体大小
     * @return 条形码图片
     */
    private static BufferedImage createWithWords(
        BufferedImage codeImage,
        String bottomStr,
        String titleStr,
        String topRightStr,
        int pictureWidth,
        int pictureHeight,
        int margin,
        int fontSize) {
        BufferedImage picImage = new BufferedImage(pictureWidth, pictureHeight, BufferedImage.TYPE_INT_RGB);
    
        Graphics2D g2d = picImage.createGraphics();
        // 抗锯齿
        setGraphics2D(g2d);
        // 设置白色
        setColorWhite(g2d, picImage.getWidth(), picImage.getHeight());
    
        // 条形码默认居中显示
        int codeStartX = (pictureWidth - codeImage.getWidth()) / 2;
        int codeStartY = (pictureHeight - codeImage.getHeight()) / 2 + 2 * margin;
        // 画条形码到新的面板
        g2d.drawImage(codeImage, codeStartX, codeStartY, codeImage.getWidth(), codeImage.getHeight(), null);
    
        // 给条码上下各画一条线
        // 设置颜色
        g2d.setColor(Color.LIGHT_GRAY);
        int y1 = 2 * margin + codeImage.getHeight();
        int y2 = 2 * margin + 2 * codeImage.getHeight();
        g2d.drawLine(0, y1, pictureWidth, y1);
        g2d.drawLine(0, y2, pictureWidth, y2);
    
        // 画文字到新的面板
        g2d.setColor(Color.BLACK);
        // 字体、字型、字号
        g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));
        // 文字与条形码之间的间隔
        int wordAndCodeSpacing = 3;
    
        // 底部文字(居中)
        if (StringUtils.isNotEmpty(bottomStr)) {
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(bottomStr);
            // 文字X轴开始坐标
            int strStartX = (pictureWidth - strWidth) / 2;
            if (strStartX < margin) {
                strStartX = margin;
            }
            // 文字Y轴开始坐标
            int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing;
            // 画文字
            g2d.drawString(bottomStr, strStartX, strStartY);
        }
    
        // 右上角文字
        if (StringUtils.isNotEmpty(topRightStr)) {
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(topRightStr);
            // 文字X轴开始坐标
            int strStartX = pictureWidth - strWidth - margin;
            // 文字Y轴开始坐标
            int strStartY = margin + fontSize;
            // 画文字
            g2d.drawString(topRightStr, strStartX, strStartY);
        }
    
        // 标题文字(居中)
        if (StringUtils.isNotEmpty(titleStr)) {
        	if (titleStr.length() > 11) {
                titleStr = titleStr.substring(0, 11);
            }
            // 字体、字型、字号
            int fs = (int) Math.ceil(fontSize * 1.5);
            g2d.setFont(new Font("微软雅黑", Font.PLAIN, fs));
            // 文字长度
            int strWidth = g2d.getFontMetrics().stringWidth(titleStr);
            // 文字X轴开始坐标
            int strStartX = (pictureWidth - strWidth) / 2;
            if (strStartX < margin) {
                strStartX = margin;
            }
            // 文字Y轴开始坐标
            int strStartY = codeStartY - margin;
            // 画文字
            g2d.drawString(titleStr, strStartX, strStartY);
        }
    
        g2d.dispose();
        picImage.flush();
    
        return picImage;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 设置 Graphics2D 属性 (抗锯齿)
    /**
     * 设置 Graphics2D 属性 (抗锯齿)
     *
     * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
     */
    private static void setGraphics2D(Graphics2D g2d) {
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
        Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
        g2d.setStroke(s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 设置背景为白色
    /**
     * 设置背景为白色
     *
     * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
     */
    private static void setColorWhite(Graphics2D g2d, int width, int height) {
        g2d.setColor(Color.WHITE);
        // 填充整个屏幕
        g2d.fillRect(0, 0, width, height);
        // 设置笔刷
        g2d.setColor(Color.BLACK);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 创建小号条形码(250x150)
    /**
     * 创建小号条形码(250x150)
     *
     * @param code
     * @param name
     * @return
     */
    private static BufferedImage createSmallWithWords(@NonNull String code, @NonNull String name) {
        // 条形码底部内容
        String bottomStr = code;
        // 条形码左上角内容
        //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
        String topLeftStr = name;
        // 条形码右上角内容
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        String topRightStr = dtf.format(LocalDateTime.now());
    
        // 生成条形码
        BufferedImage barCodeImage = create(bottomStr, 250, 50);
    
        return createWithWords(
            barCodeImage,
            bottomStr,
            topLeftStr,
            topRightStr,
            250,
            150,
            10,
            14);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 创建中号条形码(350x210)
    /**
     * 创建中号条形码(350x210)
     *
     * @param code
     * @param name
     * @return
     */
    private static BufferedImage createMiddleWithWords(@NonNull String code, @NonNull String name) {
        // 条形码底部内容
        String bottomStr = code;
        // 条形码左上角内容
        //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
        String topLeftStr = name;
        // 条形码右上角内容
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        String topRightStr = dtf.format(LocalDateTime.now());
    
        // 生成条形码
        BufferedImage barCodeImage = create(bottomStr, 350, 70);
    
        return createWithWords(
            barCodeImage,
            bottomStr,
            topLeftStr,
            topRightStr,
            350,
            210,
            14,
            20);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 创建大号条形码(500x300)
    /**
     * 创建大号条形码(500x300)
     *
     * @param code
     * @param name
     * @return
     */
    private static BufferedImage createBigWithWords(@NonNull String code, @NonNull String name) {
        // 条形码底部内容
        String bottomStr = code;
        // 条形码左上角内容
        //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
        String topLeftStr = name;
        // 条形码右上角内容
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        String topRightStr = dtf.format(LocalDateTime.now());
    
        // 生成条形码
        BufferedImage barCodeImage = create(bottomStr, 500, 100);
    
        return createWithWords(
            barCodeImage,
            bottomStr,
            topLeftStr,
            topRightStr,
            500,
            300,
            20,
            28);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 创建内置尺寸(小/中/大)条形码
    /**
     * 生成条形码(带文字)
     *
     * @param code 编码内容
     * @param name 名称
     * @param size 规格:小中大
     * @return
     */
    public static BufferedImage createWithWords(
        @NonNull String code,
        @NonNull String name,
        @NonNull Size size
    ) {
        switch (size) {
            case SMALL:
                return createSmallWithWords(code, name);
            case MIDDLE:
                return createMiddleWithWords(code, name);
            case BIG:
            default:
                return createBigWithWords(code, name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    四、二维码

    • 用某种特定几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息
    • 比一维条形码能存储更多信息,表示更多数据类型
    • 能够存储数字 / 字母 / 汉字 / 图片等信息
    • 可存储几百到几十KB字符

    1、生成指定尺寸二维码

    /**
     * 生成二维码(正方形)
     * @param content
     * @param sideLength 边长(px)
     * @return
     */
    public static BufferedImage create(String content, int sideLength) {
        BitMatrix matrix = null;
        // 定义二维码参数
        Map<EncodeHintType, Object> hints = new HashMap<>(3);
        // 设置编码
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        // 设置容错等级
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 设置边距,默认为5
        hints.put(EncodeHintType.MARGIN, 3);
    
        try {
            matrix = new MultiFormatWriter()
                .encode(content, BarcodeFormat.QR_CODE, sideLength, sideLength, hints);
        } catch (Exception e) {
            e.printStackTrace();
            //throw new RuntimeException("条形码内容写入失败");
        }
        return MatrixToImageWriter.toBufferedImage(matrix);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2、生成内置尺寸(小/中/大)二维码

    小号:250px;中号:350px;大号:500px;

    public static BufferedImage create(String content, Size size) {
        switch (size) {
            case SMALL:
                return create(content, 250);
            case MIDDLE:
                return create(content, 350);
            case BIG:
            default:
                return create(content, 500);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    五、保存

    • 存入文件
    /**
     * 存入文件
     *
     * @param barCodeImage
     * @return 文件名
     */
    public static String toFile(BufferedImage barCodeImage) {
        String fileName = getFileName();
        boolean isWrite = false;
        try {
            FileOutputStream outputStream = new FileOutputStream(new File(getImageDir(), fileName));
            isWrite = ImageIO.write(barCodeImage, "png", outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        if (isWrite) {
            return fileName;
        }
        return "";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 存放目录
    /**
     * 得到图片存放目录
     * 类路径下的image文件夹
     *
     * @return
     */
    private static String getImageDir() {
        String imageDir = "";
        Resource resource = new ClassPathResource("");
        try {
            String _dir = resource.getFile().getAbsolutePath();
            File file = new File(_dir, "image");
            if (!file.exists()) {
                file.mkdirs();
            }
            imageDir = file.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return imageDir;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 文件名
    /**
     * 生成文件名
     * 当前时间戳 + 两位随机数
     *
     * @return
     */
    private static String getFileName() {
        // 当前时间戳
        long second = Instant.now().getEpochSecond();
    
        // 两位随机数
        ThreadLocalRandom r = ThreadLocalRandom.current();
        int rand = r.nextInt(10, 99);
    
        return new StringBuffer().append(second).append(rand).append(".png").toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    六、识别

    /**
     * 识别图形码(条形码/二维码)
     * ***********************************************
     * ------ 条形码 ----------------------------------
     * 可以识别内容:手机号、纯数字、字母数字、网址(中大号)
     * 识别不了小号(250x150)含文字的网址条形码
     * 存储网址时用二维码,不要使用条形码
     * ***********************************************
     * ------ 二维码 ----------------------------------
     * 比条形码有优势;能识别三种尺寸的网址二维码
     * ***********************************************
     *
     * @param imageCode 图形码文件
     * @return void
     */
    public static String scan(File imageCode) {
        String text = "";
        try {
            BufferedImage image = ImageIO.read(imageCode);
            if (image == null) {
                return text;
            }
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    
            Map<DecodeHintType, Object> hints = new HashMap<>(3);
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            Result result = new MultiFormatReader().decode(bitmap, hints);
            text = result.getText();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("无法识别");
        }
        return text;
    }
    
    public static String scan(String fileName) {
        return scan(new File(getImageDir(), fileName));
    }
    
    public static String scan(String fileName, String fileParent) {
        return scan(new File(fileParent, fileName));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    七、测试类

    package com.tuwer.util;
    
    /**
     * 

    描述...

    * * @author 土味儿 * @version 1.0 * @Date 2023/6/10 */
    public class ImageCodeTest { public static void main(String[] args) { cr(); //sc(); } /** * 创建 */ private static void cr() { //String code = "13800000000"; String code = "1667320863285510145"; //String code = "abcdefg123456789012"; //String code = "http://www.baidu.com"; String title = "商品名称(超出不显示)"; System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.BarCode.createWithWords(code, title, ImageCodeUtil.Size.SMALL))); System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.BarCode.createWithWords(code, title, ImageCodeUtil.Size.MIDDLE))); System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.BarCode.createWithWords(code, title, ImageCodeUtil.Size.BIG))); //System.out.println(toFile(QrCode.create(code, 500))); //System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.QrCode.create(code, ImageCodeUtil.Size.SMALL))); //System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.QrCode.create(code, ImageCodeUtil.Size.MIDDLE))); //System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.QrCode.create(code, ImageCodeUtil.Size.BIG))); } /** * 识别 */ private static void sc() { // 扫描 // 13800000000 //String[] fs = {"168636218096.png", "168636218180.png", "168636218127.png"}; // 1667320863285510145 //String[] fs = {"168636260651.png", "168636260766.png", "168636260794.png"}; // abcdefg123456789012 //String[] fs = {"168636320566.png", "168636320750.png", "168636320743.png"}; // http://www.baidu.com String[] fs = {"168636738387.png", "168636738545.png", "168636738539.png"}; for (String f : fs) { System.out.println("-----------------"); System.out.println(ImageCodeUtil.scan(f)); } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    八、完整源码

    package com.tuwer.util;
    
    import com.google.zxing.*;
    import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
    import com.google.zxing.client.j2se.MatrixToImageWriter;
    import com.google.zxing.common.BitMatrix;
    import com.google.zxing.common.HybridBinarizer;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    import org.springframework.lang.NonNull;
    
    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.time.Instant;
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.ThreadLocalRandom;
    
    /**
     * 

    图形码工具类

    * * @author 土味儿 * @version 1.0 * @Date 2023/6/9 */
    public class ImageCodeUtil { /** * 尺寸 */ enum Size {SMALL, MIDDLE, BIG} // ====================== 条形码内部类 ======================== /** * 条形码 */ public static class BarCode { /** * 生成简单条形码(无文字) * * @param content * @param width * @param height * @return */ public static BufferedImage create(String content, int width, int height) { // 定义位图矩阵BitMatrix BitMatrix matrix = null; try { // 定义二维码参数 Map<EncodeHintType, Object> hints = new HashMap<>(3); // 设置编码 hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置容错等级 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 设置边距,默认为5 hints.put(EncodeHintType.MARGIN, 3); // 使用code_128格式进行编码生成条形码 MultiFormatWriter writer = new MultiFormatWriter(); matrix = writer.encode(content, BarcodeFormat.CODE_128, width, height, hints); } catch (WriterException e) { e.printStackTrace(); //throw new RuntimeException("条形码内容写入失败"); } return MatrixToImageWriter.toBufferedImage(matrix); } /** * 生成条形码(含文字) * **************************************************** * ---------------------------------------------- * | 2023-06-10 10:55 | * | | * | 商品名称 /超出不显示/ | * | | * | | || ||| | || |||| | | | ||| | | || | * | | || ||| | || |||| | | | ||| | | || | * | | || ||| | || |||| | | | ||| | | || | * | | || ||| | || |||| | | | ||| | | || | * | 13800000000 | * ---------------------------------------------- * =================================================== * 1、日期:顶部右侧 * 2、商品名称:居中 * 3、条形码:商品名称下方,且居中 * 4、条码内容:条形码下方,且居中 * ***************************************************** * * @param codeImage 条形码图片 * @param bottomStr 底部文字 * @param titleStr 标题文字 * @param topRightStr 右上角文字 * @param pictureWidth 图片宽度 * @param pictureHeight 图片高度 * @param margin 边距 * @param fontSize 字体大小 * @return 条形码图片 */ private static BufferedImage createWithWords( BufferedImage codeImage, String bottomStr, String titleStr, String topRightStr, int pictureWidth, int pictureHeight, int margin, int fontSize) { BufferedImage picImage = new BufferedImage(pictureWidth, pictureHeight, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = picImage.createGraphics(); // 抗锯齿 setGraphics2D(g2d); // 设置白色 setColorWhite(g2d, picImage.getWidth(), picImage.getHeight()); // 条形码默认居中显示 int codeStartX = (pictureWidth - codeImage.getWidth()) / 2; int codeStartY = (pictureHeight - codeImage.getHeight()) / 2 + 2 * margin; // 画条形码到新的面板 g2d.drawImage(codeImage, codeStartX, codeStartY, codeImage.getWidth(), codeImage.getHeight(), null); // 给条码上下各画一条线 // 设置颜色 g2d.setColor(Color.LIGHT_GRAY); int y1 = 2 * margin + codeImage.getHeight(); int y2 = 2 * margin + 2 * codeImage.getHeight(); g2d.drawLine(0, y1, pictureWidth, y1); g2d.drawLine(0, y2, pictureWidth, y2); // 画文字到新的面板 g2d.setColor(Color.BLACK); // 字体、字型、字号 g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize)); // 文字与条形码之间的间隔 int wordAndCodeSpacing = 3; // 底部文字(居中) if (StringUtils.isNotEmpty(bottomStr)) { // 文字长度 int strWidth = g2d.getFontMetrics().stringWidth(bottomStr); // 文字X轴开始坐标 int strStartX = (pictureWidth - strWidth) / 2; if (strStartX < margin) { strStartX = margin; } // 文字Y轴开始坐标 int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing; // 画文字 g2d.drawString(bottomStr, strStartX, strStartY); } // 右上角文字 if (StringUtils.isNotEmpty(topRightStr)) { // 文字长度 int strWidth = g2d.getFontMetrics().stringWidth(topRightStr); // 文字X轴开始坐标 int strStartX = pictureWidth - strWidth - margin; // 文字Y轴开始坐标 int strStartY = margin + fontSize; // 画文字 g2d.drawString(topRightStr, strStartX, strStartY); } // 标题文字(居中) if (StringUtils.isNotEmpty(titleStr)) { // 字体、字型、字号 int fs = (int) Math.ceil(fontSize * 1.5); g2d.setFont(new Font("微软雅黑", Font.PLAIN, fs)); // 文字长度 int strWidth = g2d.getFontMetrics().stringWidth(titleStr); // 文字X轴开始坐标 int strStartX = (pictureWidth - strWidth) / 2; if (strStartX < margin) { strStartX = margin; } // 文字Y轴开始坐标 int strStartY = codeStartY - margin; // 画文字 g2d.drawString(titleStr, strStartX, strStartY); } g2d.dispose(); picImage.flush(); return picImage; } /** * 设置 Graphics2D 属性 (抗锯齿) * * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制 */ private static void setGraphics2D(Graphics2D g2d) { g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT); Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); g2d.setStroke(s); } /** * 设置背景为白色 * * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制 */ private static void setColorWhite(Graphics2D g2d, int width, int height) { g2d.setColor(Color.WHITE); // 填充整个屏幕 g2d.fillRect(0, 0, width, height); // 设置笔刷 g2d.setColor(Color.BLACK); } /** * 创建小号条形码(250x150) * * @param code * @param name * @return */ private static BufferedImage createSmallWithWords(@NonNull String code, @NonNull String name) { // 条形码底部内容 String bottomStr = code; // 条形码左上角内容 //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11); String topLeftStr = name; // 条形码右上角内容 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); String topRightStr = dtf.format(LocalDateTime.now()); // 生成条形码 BufferedImage barCodeImage = create(bottomStr, 250, 50); return createWithWords( barCodeImage, bottomStr, topLeftStr, topRightStr, 250, 150, 10, 14); } /** * 创建中号条形码(350x210) * * @param code * @param name * @return */ private static BufferedImage createMiddleWithWords(@NonNull String code, @NonNull String name) { // 条形码底部内容 String bottomStr = code; // 条形码左上角内容 //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11); String topLeftStr = name; // 条形码右上角内容 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); String topRightStr = dtf.format(LocalDateTime.now()); // 生成条形码 BufferedImage barCodeImage = create(bottomStr, 350, 70); return createWithWords( barCodeImage, bottomStr, topLeftStr, topRightStr, 350, 210, 14, 20); } /** * 创建大号条形码(500x300) * * @param code * @param name * @return */ private static BufferedImage createBigWithWords(@NonNull String code, @NonNull String name) { // 条形码底部内容 String bottomStr = code; // 条形码左上角内容 //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11); String topLeftStr = name; // 条形码右上角内容 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); String topRightStr = dtf.format(LocalDateTime.now()); // 生成条形码 BufferedImage barCodeImage = create(bottomStr, 500, 100); return createWithWords( barCodeImage, bottomStr, topLeftStr, topRightStr, 500, 300, 20, 28); } /** * 生成条形码(带文字) * * @param code 编码内容 * @param name 名称 * @param size 规格:小中大 * @return */ public static BufferedImage createWithWords( @NonNull String code, @NonNull String name, @NonNull Size size ) { switch (size) { case SMALL: return createSmallWithWords(code, name); case MIDDLE: return createMiddleWithWords(code, name); case BIG: default: return createBigWithWords(code, name); } } } // ====================== 二维码内部类 ======================== /** * 二维码 */ public static class QrCode { /** * 生成二维码(正方形) * * @param content * @param sideLength 边长(px) * @return */ public static BufferedImage create(String content, int sideLength) { BitMatrix matrix = null; // 定义二维码参数 Map<EncodeHintType, Object> hints = new HashMap<>(3); // 设置编码 hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置容错等级 hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 设置边距,默认为5 hints.put(EncodeHintType.MARGIN, 3); try { matrix = new MultiFormatWriter() .encode(content, BarcodeFormat.QR_CODE, sideLength, sideLength, hints); } catch (Exception e) { e.printStackTrace(); //throw new RuntimeException("条形码内容写入失败"); } return MatrixToImageWriter.toBufferedImage(matrix); } public static BufferedImage create(String content, Size size) { switch (size) { case SMALL: return create(content, 250); case MIDDLE: return create(content, 350); case BIG: default: return create(content, 500); } } } // ======================== 存入文件 ========================= /** * 存入文件 * * @param barCodeImage * @return 文件名 */ public static String toFile(BufferedImage barCodeImage) { String fileName = getFileName(); boolean isWrite = false; try { FileOutputStream outputStream = new FileOutputStream(new File(getImageDir(), fileName)); isWrite = ImageIO.write(barCodeImage, "png", outputStream); } catch (IOException e) { e.printStackTrace(); } if (isWrite) { return fileName; } return ""; } /** * 得到图片存放目录 * 类路径下的image文件夹 * * @return */ private static String getImageDir() { String imageDir = ""; Resource resource = new ClassPathResource(""); try { String _dir = resource.getFile().getAbsolutePath(); File file = new File(_dir, "image"); if (!file.exists()) { file.mkdirs(); } imageDir = file.getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } return imageDir; } /** * 生成文件名 * 当前时间戳 + 两位随机数 * * @return */ private static String getFileName() { // 当前时间戳 long second = Instant.now().getEpochSecond(); // 两位随机数 ThreadLocalRandom r = ThreadLocalRandom.current(); int rand = r.nextInt(10, 99); return new StringBuffer().append(second).append(rand).append(".png").toString(); } // ======================= 识别图形码 ======================== /** * 识别图形码(条形码/二维码) * *********************************************** * ------ 条形码 ---------------------------------- * 可以识别内容:手机号、纯数字、字母数字、网址(中大号) * 识别不了小号(250x150)含文字的网址条形码 * 存储网址时用二维码,不要使用条形码 * *********************************************** * ------ 二维码 ---------------------------------- * 比条形码有优势;能识别三种尺寸的网址二维码 * *********************************************** * * @param imageCode 图形码文件 * @return void */ public static String scan(File imageCode) { String text = ""; try { BufferedImage image = ImageIO.read(imageCode); if (image == null) { return text; } LuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Map<DecodeHintType, Object> hints = new HashMap<>(3); hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE); hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); Result result = new MultiFormatReader().decode(bitmap, hints); text = result.getText(); } catch (Exception e) { e.printStackTrace(); System.out.println("无法识别"); } return text; } public static String scan(String fileName) { return scan(new File(getImageDir(), fileName)); } public static String scan(String fileName, String fileParent) { return scan(new File(fileParent, fileName)); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499

    参考自:Java生成读取条形码及二维码

  • 相关阅读:
    Kotlin 数据类型详解:数字、字符、布尔值与类型转换指南
    一文详解扩散模型:DDPM
    拯救SQL Server数据库事务日志文件损坏的终极大招
    QT QTableView 委托:垂直表头
    林木种苗生产vr虚拟实训教学降低培训等待周期
    基于PHP的校园二手信息网站的设计与实现毕业设计源码251656
    了解Qt QScreen的geometry ,size
    HTML CSS JS 及上下左右键变化
    K8S架构原理
    一起探秘,不可不知双向链表底层原理
  • 原文地址:https://blog.csdn.net/tu_wer/article/details/131140623