关于“使用java中的二维矩阵方法生成二维码“ 以及 “Java加载外部字体文件时出错的原因“

news2024/12/27 10:24:46

生成二维码

铁铁们,这两日写了一个导出二维码的接口,要求有一个是在二维码下方生成字体,现在奉上生成二维码的代码:
controller层

 @Operation(summary = "导出机构二维码",description = "导出机构二维码")
    @GetMapping("/orgCode")
    public void getOr(@RequestParam("url") String url,@RequestParam("orgIds") List<Long> orgIds,@RequestParam("channels") List<String> channels, HttpServletResponse response) throws IOException{
        //返回二维码生成的数据
        byte[] data = orgCodeService.downLoadOrg(url, orgIds, channels);
        response.reset();
        //指定返回的文件为附件形式,指定文件名为"二维码zip"
        response.setHeader(org.springframework.http.HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode("二维码.zip","UTF-8"));
        //设置返回数据的长度
        response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
        //设置相应类型为二进制流
        response.setContentType("application/octet-stream; charset=UTF-8");
        //将二维码数据写入响应的输出流中,完成文件下载操作
        IoUtil.write(response.getOutputStream(), Boolean.TRUE, data);
    }

service层:

    byte[] downLoadOrg(String url,List<Long> orgIdList,List<String> channels) throws IOException;

实现类:

@Slf4j
@Service
public class QRGCodeServiceImpl implements QRGCodeService {

    // 设置二维码的默认宽度和高度
    private final static int width = 756;
    private final static int height = 850;

    // 设置二维码缩放比例和文件类型
    private final static int SCALE = 2;
    private final static String fileType = "png";

    // 设置字体大小、圆角半径和文字位置
    private final static int fontSize = 50;
    private final static int roundingRadius = 250;
    private final static int pixel = 50;
    private final static int FONT_SIZE_BIG = 23 * SCALE;

    @Resource
    private OrgOrganizationService orgOrganizationService;

    /**
     * @author dongruipeng
     * @date 2024/4/10
     * @description 根据URL,机构ID列表和渠道列表生成二维码后压缩并返回给前端
     * @param
     */
    @Override
    public byte[] downLoadOrg(String url, List<Long> orgIdList, List<String> channels) throws IOException {
        //参数校验
        Objects.requireNonNull(url, "url can not be null");
        Objects.requireNonNull(orgIdList, "orgIdList can not be null");
        Objects.requireNonNull(channels, "channels cannot be null");
        url = URLDecoder.decode(url, StandardCharsets.UTF_8);
        log.info("url参数:{}", url);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try (ZipOutputStream zos = new ZipOutputStream(stream)) {
            for (Long orgId : orgIdList) {
                // 获取机构名称
                String orgName = getOrgName(orgId);
                for (String channel : channels) {
                    String qrContent = url + "?orgId=" + orgId + "&channel=" + channel;
                    Color color = getColorByChannel(channel);
                    byte[] qrCodeBytes = generateQRCode(qrContent, channel.equals(OrgChannelConstant.ONSITE_QR_CODE), orgName, color);
                    String fileName = orgName + "_" + OrgCodeConstant.getByCode(Integer.parseInt(channel)) + ".png";
                    ZipEntry entry = new ZipEntry(fileName);
                    zos.putNextEntry(entry);
                    zos.write(qrCodeBytes);
                    zos.closeEntry();
                }
            }
        }
        return stream.toByteArray();
    }

    // 获取机构名称
    private String getOrgName(Long orgId) {
        OrganizationVo organizationVo = orgOrganizationService.selectByOrgId(orgId);
        Assert.notNull(organizationVo, "该机构信息不存在");
        return organizationVo.getName();
    }

    // 生成二维码图片
    private byte[] generateQRCode(String qrContent, boolean isSharpCorner, String orgName, Color color) {
        try {
            //设置二维码参数
            QRCodeWriter qrCodeWriter = new QRCodeWriter();
            BitMatrix bitMatrix = qrCodeWriter.encode(qrContent, BarcodeFormat.QR_CODE, width, height);

            //创建BufferedImage对象,并设置背景颜色
            BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D graphics = bufferedImage.createGraphics();
            graphics.setColor(Color.WHITE);
            graphics.fillRect(0, 0, width, height);
            graphics.setColor(color);

            //将BitMatrix - 二维矩阵 对象转换为BufferedImage对象
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    if (bitMatrix.get(i, j)) {
                        graphics.fillRect(i, j, 1, 1);
                    }
                }
            }

            Font font;
            File yuanti = new File("/usr/share/fonts/SIMSUN.TTC");
            try {
                if (yuanti.exists()) {
                    font = Font.createFont(Font.TRUETYPE_FONT, new File("/usr/share/fonts/SIMSUN.TTC"));
                    font = font.deriveFont(Font.BOLD, FONT_SIZE_BIG);
                    log.info("加载宋体字体文件成功");
                } else {
                    font = new Font("宋体", Font.BOLD, FONT_SIZE_BIG);
                }
            } catch (IOException | FontFormatException e) {
                log.error("加载字体文件时出错: {}", e.getMessage());
                font = new Font("宋体", Font.BOLD, FONT_SIZE_BIG); //加载失败时使用默认字体
            }


            graphics.setFont(font);
            FontMetrics fontMetrics = graphics.getFontMetrics(font);
            int orgNameWidth = fontMetrics.stringWidth(orgName);
            //计算居中的X坐标
            int orgNameX = (width - orgNameWidth) / 2;
            //调整像素,文本接近底部
            int orgNameY = height - pixel;
            //机构名称居中
            graphics.drawString(orgName, orgNameX, orgNameY);

            //设置二维码边框白色圆角
            if (!isSharpCorner) {
                //圆角半径
                BufferedImage roundedImage = makeRoundedCorner(bufferedImage, roundingRadius);
                bufferedImage = roundedImage;
            }

            // 保存二维码图片到字节数组
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                ImageIO.write(bufferedImage, fileType, outputStream);
                return outputStream.toByteArray();
            } catch (IOException e) {
                log.error("保存二维码图片时出错: {}", e.getMessage());
                throw new RuntimeException("保存二维码图片时出错", e);
            }

        } catch (WriterException e) {
            log.error("生成二维码时出错: {}", e.getMessage());
            throw new RuntimeException("生成二维码时出错", e);
        }
    }

    //创建圆角图片
    private static BufferedImage makeRoundedCorner(BufferedImage image, int cornerRadius) {
        //获取图像宽高
        int width = image.getWidth();
        int height = image.getHeight();
        //创建新的透明背景的 BufferedImage 对象,存储有圆角的图像
        BufferedImage roundedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        //创建 Graphics2D 对象绘制图像
        Graphics2D g2 = roundedImage.createGraphics();
        //设置图片合成模式为 Src,用于在创建新的图形之前清除现有的
        g2.setComposite(AlphaComposite.Src);
        //设置抗锯齿,确保图形边缘平滑
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        //设置图片颜色为白色,用于设置圆角矩形作为图像的背景
        g2.setColor(Color.WHITE);
        //绘制圆角矩形,作为图像的背景,cornerRadius参数确定圆角的大小
        g2.fill(new RoundRectangle2D.Float(0, 0, width, height, cornerRadius, cornerRadius));
        //设置合成模式为 SrcAtop,在现有图形上绘制新内容
        g2.setComposite(AlphaComposite.SrcAtop);
        //在背景图像上绘制原来的图像,使该图带圆角
        g2.drawImage(image, 0, 0, null);
        //释放资源
        g2.dispose();
        return roundedImage;
    }

    // 根据渠道设置颜色
    private Color getColorByChannel(String channel) {
        switch (channel) {
            case "1":
                return Color.BLACK;
            case "2":
                return new Color(37, 82, 151); //#255297
            default:
                return Color.BLACK;
        }
    }
}

上述代码中,可谓是耗费了我十足的精力,在自己本地测试时,因为我们的系统文件,也就是"C:\Windows Fonts文件夹"下面存放的字体文件十分之多,如下图:
在这里插入图片描述

所以如果自己本地使用postman测试的话,上述代码中二维码生成的下方文字无论如何也不会乱码,但是一旦发送至我们的服务器上,如果虚拟服务器上的"/usr/share/fonts/"文件夹下面没有字体文件的话,前端一调接口,就会产生乱码问题,那么如何解决呢?

1.首先查看自己引入的字体文件是否已经破坏,最好的测试方法就是:自己在postman上测试,如果生成的字体是系统默认的,那就是坏的,如果生成的字体是我们想要的字体,例如代码所示,使用该方法排除是否字体文件已经破坏
在这里插入图片描述
2.如果在确保字体文件没有被破坏的情况下还是无法加载成功,那么这个时候我们就需要在pom文件当中添加防止打包jar包时能够不被java过滤的依赖,如下图:

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <configuration>
            <!-- 过滤后缀不需要转码的文件后缀名.crt-->
            <nonFilteredFileExtensions>
                <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                <nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
                <nonFilteredFileExtension>xls</nonFilteredFileExtension>
                <nonFilteredFileExtension>zip</nonFilteredFileExtension>
                <nonFilteredFileExtension>cer</nonFilteredFileExtension>
                <nonFilteredFileExtension>pfx</nonFilteredFileExtension>
                <nonFilteredFileExtension>py</nonFilteredFileExtension>
            </nonFilteredFileExtensions>
        </configuration>
    </plugin>
</plugins>

在这里插入图片描述
3.在确保文件没有被破坏和已经加载了依赖的情况下还是无法加载成功的话,这个时候我们就需要查看服务器上的文件夹下是否存在我们需要的字体文件,首先,在DockerFile文件中添加一行Copy外部文件的命令

COPY ./SIMSUN.TTC /usr/share/fonts/SIMSUN.TTC

我存放的目录是这样的:
在这里插入图片描述

一定要确保我们的服务器上文件夹下有我们要的字体文件才可以,如果你们遇到了这种情况,一定要排查!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1590035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Session缓存、Hibernate处理对象的状态了解

Session接口 Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口&#xff0c;它提供了基本的保存&#xff0c;更新&#xff0c;删除和查询的方法。 Session是有一个缓存, 又叫Hibernate的一级缓存 session缓存是由一系列的Java集合构成的。当一个对象被加入到…

element table 使用了表格固定height高度和表格属性fixed属性出现了高度错乱bug

问题描述&#xff1a;因为使用了表格固定height高度所以 使用表格属性fixed属性出现了高度错乱bug知识点&#xff1a;使用element table 里提供的doLayout 方法 代码 // template <el-table ref"test" ></el-table>//js// 查询数据getList(obj{}) {get…

当你的项目体积比较大?你如何做性能优化

在前端开发中&#xff0c;项目体积优化是一个重要的环节&#xff0c;它直接影响到网页的加载速度和用户体验。随着前端项目越来越复杂&#xff0c;引入的依赖也越来越多&#xff0c;如何有效地减少最终打包文件的大小&#xff0c;成为了前端工程师需要面对的挑战。以下是一些常…

008Node.js模块、自定义模块和CommonJs

CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API&#xff0c;从而填补了这个空白。它的终极目标是提供一个类似Python&#xff0c;Ruby和Java标 准库。这样的话&#xff0c;开发者可以使用CommonJS API编写应用程序&#xff0c;然后这些应用可以运行在不同的…

时钟周期检测标志信号

在某些情况下需要对系统时钟分频后的时钟进行周期检测&#xff0c;引出周期标志信号以便在后续其他情况的使用。虽然在大多数情况下我们能够知道分频后的时钟是系统时钟的几倍分频&#xff0c;但为增强在分频时钟改变情况下周期标志信号的复用性或对未知时钟的周期检测&#xf…

第六季:RTSP协议详解与实时流视频预览

目录 前言1 环境准备2 H.264编码原理和基本概念2.1 图像冗余信息2.2 h.264编码相关的一些概念2.3 h264视频流总体分析2.4 H264的NAL单元详解22.4.1 相关概念 2.5 NALU详解2.6 sps和pps详解2.7 H264的profile和level2.8 序列sequence 前言 本篇文章用于记录实验过程 1 环境准备…

【YUNBEE云贝-进阶课】MySQL8.0性能优化实战培训

众多已经学习过MySQL 8.0 OCP认证专家的课程的同学们对 MySQL 8.0 的安装部署、体系结构、配置监控、用户管理、主从复制、系统运维、MGR等基础操作和动手实验有了一定的学习基础.很多学员反馈希望更进一步提升技术能力、解决工作中碰到的性能问题。 针对MySQL8.0的数据库性能优…

设计模式代码实战-建造者模式

1、问题描述 小明家新开了一家自行车工厂&#xff0c;用于使用自行车配件&#xff08;车架 frame 和车轮 tires &#xff09;进行组装定制不同的自行车&#xff0c;包括山地车和公路车。 山地车使用的是Aluminum Frame&#xff08;铝制车架&#xff09;和 Knobby Tires&#x…

softmax回归:多分类问题的解码器

随着人工智能技术的不断发展&#xff0c;分类问题在机器学习领域中的地位日益凸显。在众多分类算法中&#xff0c;softmax回归以其独特的优势和广泛的应用场景&#xff0c;成为了处理多分类问题的有力工具。本文将深入探讨softmax回归的原理、应用及其优缺点&#xff0c;以期为…

实现智能水控 | 基于ACM32 MCU的分体式水控方案

分体式水控概述 分体式水控是一种常见的水控系统&#xff0c;它的工作原理是通过水的流动来控制水的供应和排放&#xff0c;该系统一般由两部分组成&#xff1a;控制器和水阀。控制器负责监测水的流量和压力&#xff0c;根据设定的参数来控制水阀的开和关&#xff0c;从而实现水…

上位机图像处理和嵌入式模块部署(qmacvisual缺失的光源控制)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 有些场景下面&#xff0c;是不需要光源和光源控制的&#xff0c;比如说利用摄像头识别对应区域的库位&#xff0c;这部分直接利用红外光采集对应的…

找出mongodb的jumbo块并进行分裂

https://www.cnblogs.com/abclife/p/15968628.html 根据这篇文档中的脚本&#xff0c;在我们自己的环境中跑了下&#xff0c;第一次跑的结果如下&#xff1a; 运行完上面跑出的split脚本后&#xff0c;还是存在jumbo块&#xff0c;第二次跑出的结果&#xff1a; 从上面结果可以…

3、JVM对象的创建于内存分配原理

对象的创建 对象创建的主要流程: 1.类加载检查 虚拟机遇到一条new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有&#xff0c;那必须先执行相应的类加…

【hive】远程remote debug hive的方法,用于hive监听器/钩子编写

背景 写hive监听器时候需要拿到hive对象但hive是在集群linux主机上运行的。通过jdbc提交的sql具体执行过程不会再idea中运行。所以如果需要拿到hive对象有可能存在两个思路&#xff1a; &#xff08;1&#xff09;想办法写个钩子或者监听器&#xff0c;将需要的内容写成json字…

【微信小程序】canvas开发笔记

【微信小程序】canvasToTempFilePath:fail fail canvas is empty 看说明书 最好是先看一下官方文档点此前往 如果是canvas 2d 写canvas: this.canvas,&#xff0c;如果是旧版写canvasId: ***, 解决问题 修改对应的代码&#xff0c;如下所示&#xff0c;然后再试试运行&#x…

请求分发场景下的鉴权问题

说明&#xff1a;记录一次对请求分发&#xff0c;无法登录系统的问题。 场景 如下&#xff0c;在此结构下&#xff0c;如何判断该用户是已登录的用户&#xff1b; 常规操作&#xff0c;用户登录后给用户发Token&#xff0c;同时将发放的Token存入到Redis中。要求用户后续请求…

鸿蒙OS开发实例:【Native C++】

介绍 本篇Codelab主要介绍如何使用DevEco Studio创建一个Native C应用。应用采用Native C模板&#xff0c;实现使用NAPI调用C标准库的功能。使用C标准库hypot接口计算两个给定数平方和的平方根。在输入框中输入两个数字&#xff0c;点击计算结果按钮显示计算后的数值。 相关概…

【论文研读】Geometric Deep Learning on Molecular Representations

Geometric Deep Learning on Molecular Representationshttps://arxiv.org/pdf/2107.12375.pdf 一、Background 随着网络时代的发展&#xff0c;生活中产生的数据量越来越多&#xff0c;但数据大体分为两类&#xff1a;欧氏数据、非欧氏数据。如图为两类常见的数据&#xff0c…

物联网全栈智能应用实训系统

物联网全栈智能应用实训系统是一款集硬件、软件、网络、数据分析与应用开发于一体的综合性实训平台。它旨在帮助学习者全面掌握物联网技术的各个环节&#xff0c;从硬件设备选型、通信协议理解、软件开发、数据分析到应用部署&#xff0c;都能得到充分的实践锻炼。 一、产品构…

Harmony鸿蒙南向驱动开发-SDIO接口使用

功能简介 SDIO是安全数字输入输出接口&#xff08;Secure Digital Input and Output&#xff09;的缩写&#xff0c;是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD卡&#xff0c;并且可以连接支持SDIO接口的其他设备。 SDIO接口定义了操作SDIO的通用…