当前位置: 首页 > news >正文

【图片处理】✈️HTML转图片字体异常处理

💥💥✈️✈️欢迎阅读本文章❤️❤️💥💥

🏆本篇文章阅读大约耗时5分钟。

⛳️motto:不积跬步、无以千里

📋📋📋本文目录如下:🎁🎁🎁

目录

前言

问题排查

寻找方案

解决方案

实现步骤

章末

前言

        小伙伴们大家好,最近开发过程中遇到一个比较折磨的小问题,正如文章的标题所述,是在java项目中将 html 文件转换为图片中遇到的,具体的异常和排查后文会提及,也可以直接跳到最后查看解决方案(不一定能百分百解决各位的问题,但是可以尝试下)

功能概要

        需要返回给前端一个图片的下载链接,图片的要求是按照特殊格式排版的,并且图中会有很多自定义变量,基本每张图片的变量填充都不一样

        原始方案:使用 html 搭建出基本的图片结构,再将 html 转换为 图片

        1. 使用 Java2DRenderer 工具实现 html 转换为图片

        2. 使用 Themeleaf 工具实现 html 中待填充的变量灵活替换

问题乍现:按照开发流程在本地开发完成之后,测试没什么问题,接着发布到测试环境,出现了以下问题(测试环境服务器属于 Centos linux)

        1.服务器上生成的图片大小与本地生成的图片相差很多,本地 50kb的,服务器生成的只有 7kb左右

        2.服务器生成的图片质量较差,并且字体明显不对,大概如下(左边本地,右边服务器)

问题排查

        1.排除代码问题,代码都是同一套

        2.环境问题:服务器环境和本地差异很大

                2.1 代码中尝试指定渲染图片时使用指定的字体,经测试不生效

                2.2 服务器安装指定字体,服务器新增字体后,经测试还是没解决

                2.3 html 源码中指定使用字体,经测试不生效

                2.4 代码中生成图片的工具,切换为别的sdk后本地测试没问题,服务器上测试问题依然存在

寻找方案

        1.市面上各种 ai 查询解决方案和替代方案,一开始都徒劳无果

        2.各大博客寻找相似问题未果,可能这种问题遇到的人比较少

        3.摆烂,就这样了

解决方案

        多亏别人提醒,回想之前的方案都是将html使用代码转换成一张图片,这里面会经过服务器配置去渲染,问题出现在服务器渲染的时候,那么何不直接换种方式,已经有html了,那直接通过代码调用服务器的应用程序打开html文件,然后直接截图一张不就跳过上面那种问题了,实测一下,问题解决

        大概就是使用 Headless Chrome 将 HTML 打开,然后代码控制直接截图

实现步骤

        1.服务器安装浏览器和想要使用的字体

                1.1服务器更新安装工具

        sudo yum update  (更新成功就不用执行这条下载命令了sudo yum install epel-release -y)

                1.2 安装 Chromium 浏览器

        sudo yum install chromium -y

                1.3 安装指定字体

                目标使用用 Arial 字体,需要将该字体的文件上传到服务器(可自行查询服务器安装字体教程),也可以看一下之前服务器的字体都是什么,除自行安装的 Arial 就是 DejaVu 字体了,图片上生成的字符使用的应该就是这种字体了,看着很别扭

        2. 将获取图片方法 封装为一个工具类

import lombok.extern.slf4j.Slf4j;
import org.hibernate.service.spi.ServiceException;
import org.springframework.stereotype.Component;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;/*** @author benbenhuang* @date 2025年10月16日 22:03*/
@Slf4j
@Component
public class HtmlUtil {/*** 使用 Headless Chrome 将 HTML 渲染为 PNG 并上传*/public void convertHtmlToImageAndUpload(String htmlContent, String fileName) {Path htmlPath = null;Path outputPath = null;try {//生成临时 HTML 文件htmlPath = Files.createTempFile("page_", ".html");Files.write(htmlPath, htmlContent.getBytes(StandardCharsets.UTF_8));//生成临时输出图片路径outputPath = Files.createTempFile("screenshot_", ".png");//组装 Chrome 命令String chromePath = detectChromeBinary();ProcessBuilder pb = new ProcessBuilder(chromePath,"--headless","--disable-gpu","--no-sandbox","--hide-scrollbars","--window-size=350,550","--screenshot=" + outputPath.toAbsolutePath(),htmlPath.toAbsolutePath().toString());pb.redirectErrorStream(true);Process process = pb.start();//打印 Chrome 输出日志(方便排错)try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {reader.lines().forEach(line -> log.debug("[chrome] {}", line));}int exitCode = process.waitFor();if (exitCode != 0) {throw new RuntimeException("Chrome render failed, exit code = " + exitCode);}//转换为 Base64byte[] imageBytes = Files.readAllBytes(outputPath);String base64Image = Base64.getEncoder().encodeToString(imageBytes);//            //上传到 OSS
//            ossclient.uploadPic(base64Image, fileName);} catch (Exception e) {throw new ServiceException("Render HTML to image failed");} finally {// 清理临时文件try {if (htmlPath != null) Files.deleteIfExists(htmlPath);if (outputPath != null) Files.deleteIfExists(outputPath);} catch (IOException ignored) {}}}/*** 检测 Chrome 可执行路径*/private String detectChromeBinary() {String[] candidates = {"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", // macOS 主要路径"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary", // macOS Canary版本"/Applications/Chromium.app/Contents/MacOS/Chromium", // macOS Chromium路径"/usr/bin/google-chrome", // Linux 路径"/usr/bin/chromium-browser", // Linux 路径"/usr/local/bin/chrome", // 可能的自定义路径"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" // Windows 路径};for (String path : candidates) {if (Files.exists(Paths.get(path))) {return path;}}return "google-chrome";}
}

        3. 这一个工具类别的就没了,看下入参,一个是 html 的代码字符串(已经处理过变量填充问题),一个是 fileName,因为本地是需要将图片上传到oss的,当然,也可以调整为输出到本地,,只需要调整一下最后的图片上传操作即可

        检测可执行路径方法就是找到该浏览器到的可执行路径,里面添加了不同系统该软件默认安装后的可执行路径

        整体就是通过浏览器打开临时html文件,截图保存即可

        经测试生成的图片没有问题了,图片大小和本地大差不差,字体也是目标字体

章末

        文章到这里就结束了~

往期推荐 > > > 

 【服务器搭建】✈️用自己电脑搭建一个服务器!

 【IDEA】✈️自定义模板,自动生成类和方法注释

 【日志链路】⭐️SpringBoot 整合 TraceId 日志链路追踪!

http://www.dtcms.com/a/490624.html

相关文章:

  • Visual Studio 命令和属性的常用宏定义(macros for MSBuild commands and properties)
  • Android 中 gravity 与 layout_gravity 的深度解析:从概念到实践
  • 免费的招标网站有哪些wordpress编辑器上传图片
  • Spring初始
  • VB.Net循序渐进(第二版)
  • AI预判等离子体「暴走」,MIT等基于机器学习实现小样本下的等离子体动力学高精度预测
  • 网站链接推广工具网站提现功能怎么做
  • list的迭代器
  • 学会网站制作要多久网站建设最重要的是什么
  • 基于遗传算法优化BP神经网络(GA-BP)的数据时序预测
  • Mamba革命:图像增强的下一站,从CNN与Transformer到状态空间模型的跨
  • 利用Enterprise Architect的需求管理工具实现项目全程可追溯性
  • 我的个人云端革命:从依赖公有云到自建私有云的蜕变
  • Qi标准无线充调试记录
  • 数据结构5:线性表5-循环链表
  • 双生态城市:跨物种和谐共居的未来图景-光影交织的和谐之地
  • 合肥 做网站的百度网页大全
  • 安徽建设厅网站官网网站设计制作系统哪个好
  • 【electron6】Web Audio + AudioWorklet PCM 实时采集噪音和模拟调试
  • Ajax 详解
  • 网站开发表格wordpress 语法编辑
  • 《零踩坑教程:基于 Trae 的高德地图 API 部署全流程解析》
  • 在Linux服务器上使用Jenkins和Poetry实现Python项目自动化
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段-二阶段(3):文法運用
  • [Sora] 数据管理 | `group_by_bucket`智能分桶 | DataloaderForVideo数据传输
  • 反向代理原理和服务转发实现方式
  • 中山企业网站多少钱爱奇艺做视频网站的
  • mapper.xml sql动态表查询配置
  • SQL Server 2019实验 │ 设计数据库的完整性
  • Leetcode每日一练--35