复杂html动态页面高还原批量导出方案
复杂html动态页面高还原批量导出方案
- 方案一
- 方案二
方案一
- thymeleaf
thymeleaf 负责服务端渲染,输出html静态页面。
- wkhtmltopdf
wkhtmltopdf 是一个基于 WebKit 的命令行工具,能够将 HTML 转换为 PDF。
支持复杂的 HTML 和 CSS。
缺点:
- wkhtmltopdf软件需要提前安装,增加系统部署复杂度。
- WebKit引擎的渲染结果和浏览器有差异,可能需要调整html的兼容性。
- 其它
服务端缓存导出记录,下次导出同一份数据,直接命中对象存储中的文件。
<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
public void generatePdf() throws Exception {
// Create the Thymeleaf context and set variables
Context context = new Context();
MyData data = myService.queryData(100L);
context.setVariable("data", data);
// Process the Thymeleaf template
String htmlContent = templateEngine.process("report", context);
FileUtil.writeUtf8String(htmlContent, "D:/data/tmp/input.html");
// 调用 wkhtmltopdf 命令行工具
Process process = Runtime.getRuntime().exec("D:/Program Files/wkhtmltopdf/bin/wkhtmltopdf D:/data/tmp/input.html D:/data/tmp/output.pdf");
process.waitFor();
}
针对缺点2,可以使用
Cursor
AI辅助编码工具,一键改写。
- prompt
[任务背景]
需要将HTML文档通过wkhtmltopdf(v0.12.6+)转换为PDF。由于该工具基于Qt WebKit引擎,请确保生成的HTML/CSS严格遵循其兼容性要求。
[核心要求]
1. 代码必须完全兼容wkhtmltopdf的渲染限制
2. 保持原有布局的视觉一致性
3. 优先使用经wkhtmltopdf验证的替代方案
4. 输出可维护的简洁代码
[技术限制清单]
## 布局系统
- 禁用:Flexbox/Gap属性、CSS Grid、Multi-column布局
- 替代方案:表格布局(table-layout)/浮动定位(float)/绝对定位
- 间距控制:使用margin/padding代替gap属性
## CSS特性
- 禁用:CSS变量(var())、position:sticky、clip-path等新特性
- 选择器:仅支持CSS2.1基础选择器,避免:nth-child等伪类
- 盒模型:明确指定width/height,避免calc()等动态计算
## 渲染特性
- 禁用:所有CSS动画/过渡效果、3D变换
- 伪元素:限制::before/::after的使用,避免复杂内容生成
- 字体:必须嵌入base64字体,禁用woff2格式
[优化策略]
1. 布局重构:使用基于浮动的布局系统(table布局作为备选方案)
2. 样式降级:将CSS变量替换为静态值,合并重复样式
3. 渐进增强:为关键元素添加兼容层(如通过JS计算布局)
4. 输出规范:生成带版本注释的代码,标注兼容性处理点
方案二
selenium-java
优点: 使用真实的Chrome浏览器渲染,高还原度。
缺点:
- 需要安装
Chrome浏览器
和Chrome驱动
,并要保证两者的兼容性,增加系统部署复杂度。
<!-- Selenium 主库 -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.15.0</version>
</dependency>
<!-- 强制指定 Guava 版本 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>
</dependencies>
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v114.page.Page;
import org.openqa.selenium.devtools.v114.page.model.PrintToPDFTransferMode;
import java.util.Base64;
import java.util.Optional;
import java.nio.file.Files;
import java.nio.file.Paths;
public class PdfExporter {
public static void main(String[] args) {
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new");
options.addArguments("--disable-gpu");
options.addArguments("--force-device-scale-factor=1"); // 防止缩放
WebDriver driver = new ChromeDriver(options);
DevTools devTools = ((ChromeDriver) driver).getDevTools();
try {
devTools.createSession();
devTools.send(Page.enable());
driver.get("https://example.com");
Thread.sleep(2000);
// 完整参数配置(按方法定义顺序)
String pdfData = devTools.send(
Page.printToPDF(
Optional.of(false), // landscape
Optional.of(false), // displayHeaderFooter
Optional.of(true), // printBackground
Optional.of(1.0), // scale
Optional.of(8.5), // paperWidth (inches)
Optional.of(11.0), // paperHeight (inches)
Optional.of(0.4), // marginTop
Optional.of(0.4), // marginBottom
Optional.of(0.4), // marginLeft
Optional.of(0.4), // marginRight
Optional.empty(), // pageRanges (默认全部)
Optional.empty(), // headerTemplate
Optional.empty(), // footerTemplate
Optional.of(true), // preferCSSPageSize
Optional.of(PrintToPDFTransferMode.RETURNASBASE64) // transferMode
)
).getData();
Files.write(Paths.get("output.pdf"), Base64.getDecoder().decode(pdfData));
System.out.println("PDF生成成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}