报表在浏览器里看着正常,转成PDF仍可能出现截列、背景丢失或多出空白页。下面先用Gotenberg部署一个可调用的本机服务,再提交完整活动日报模板,用纸张、背景和内容检查定位输出差异。应用用途是后端报表导出,模板作为输入,PDF作为业务文件输出。
本稿按官方资料整理,未在ECS或本机Docker运行。演示两行数据共20笔、2000元,适合核对操作链,不代表真实经营数据或任何开票结果。
1. 配好本机转换服务
在准备部署的Linux主机上执行;需要已安装Docker Engine、Compose插件、curl和Bash,且3000端口未被占用。先确认连接的是预期主机,再建立独立演示目录:
docker context show
docker compose version
mkdir -p "$HOME/gotenberg-report-demo/out" || exit 1
cd "$HOME/gotenberg-report-demo" || exit 1
context不是预期环境就先停止;Compose命令不存在则先补齐插件。后续命令都在这个目录、这台Docker主机的终端执行。
创建完整compose.yaml:
cat > compose.yaml <<'YAML'
name: gotenberg-report-demo
services:
gotenberg:
image: docker.1ms.run/gotenberg/gotenberg:8.38.0
ports:
- "127.0.0.1:3000:3000"
init: true
restart: unless-stopped
YAML
docker compose config -q || exit 1
docker compose pull || exit 1
docker compose up -d || exit 1
docker compose ps
docker compose logs --tail=40 gotenberg
配置检查应无错误;拉取失败时停在该步,核对完整镜像路径、鉴权与网络,不把失败写成启动成功。ps没有服务行、显示退出或反复重启时,先看同一服务日志;端口占用则调整宿主端口并同步后续URL。这里的镜像入口只负责取得制品,不能证明转换服务已经可用。
curl -fS --connect-timeout 5 --max-time 10 \
--write-out '\nHTTP %{http_code}\n' \
http://127.0.0.1:3000/health || exit 1
健康时应返回HTTP 200,响应中总体status为up。连接被拒绝时先检查容器是否启动及端口映射;503或模块down则看日志,不进入业务转换。初始化尚未结束可稍后重试。此处仅为判读说明,非本机实测输出。
完整镜像命名空间和固定版本写进Compose,避免部署时只记住服务名。本文未验证该版本经镜像入口实际拉取成功;取到镜像之后,仍需继续健康和业务检查。
2. 用A4模板固定第一页结果
创建一份完整的报表输入。文件名保留为index.html,这是此转换接口的要求。英文标题是为了把首个演示限制在基础字体;真实中文模板需单独验字体与分页。
cat > index.html <<'HTML'
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Campaign Daily Report - Demo</title>
<style>
@page { size: A4; margin: 18mm; }
body { font-family: sans-serif; color: #172438; }
h1 { font-size: 24px; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px; border: 1px solid #b9c9df; }
th { background: #ecf4ff; }
.note { color: #596b85; font-size: 12px; }
</style>
</head>
<body>
<h1>Campaign Daily Report</h1>
<p>Date: 2026-10-27</p>
<p class="note">DEMO DATA - not a customer record or tax invoice.</p>
<table>
<tr><th>Channel</th><th>Orders</th><th>Amount (CNY)</th></tr>
<tr><td>Store A</td><td>12</td><td>1200.00</td></tr>
<tr><td>Store B</td><td>8</td><td>800.00</td></tr>
<tr><th>Total</th><th>20</th><th>2000.00</th></tr>
</table>
</body>
</html>
HTML
这20笔、2000元全为演示数据。模板已把数据写入页面,不访问业务系统,也没有外部脚本、图片或字体依赖;Gotenberg不负责计算业务汇总或核实订单真实性。
模板通过@page声明A4尺寸和页边距;调用时还要把preferCssPageSize设为true,才能让CSS纸张设置优先。背景色需要printBackground=true。这两项分别控制版面和背景,不能靠换浏览器窗口宽度替代PDF纸张设置。

3. 上传模板并保存本次PDF
把转换动作保存为一个可重用脚本。先写临时文件,只有HTTP成功且文件头符合PDF特征时才替换正式文件,避免错误响应覆盖上一份报表。
cat > convert.sh <<'SH'
#!/usr/bin/env bash
set -eu
mkdir -p out
part=$(mktemp out/report.pdf.XXXXXX)
trap 'rm -f "$part"' EXIT
if http_code=$(curl -fS --connect-timeout 5 --max-time 90 \
--form 'files=@index.html' \
--form 'preferCssPageSize=true' \
--form 'printBackground=true' \
--form 'failOnResourceLoadingFailed=true' \
--write-out '%{
http_code}' \
--output "$part" \
http://127.0.0.1:3000/forms/chromium/convert/html); then
if [ "$http_code" != "200" ]; then
printf 'Unexpected HTTP status: %s\n' "$http_code" >&2
exit 1
fi
else
curl_status=$?
printf 'Conversion failed: curl=%s HTTP=%s\n' \
"$curl_status" "$http_code" >&2
exit 1
fi
if [ "$(head -c 5 "$part")" != '%PDF-' ]; then
printf 'Response is not recognized as a PDF\n' >&2
exit 1
fi
mv "$part" out/report.pdf
printf 'Saved: out/report.pdf\n'
SH
bash convert.sh
成功分支会显示Saved: out/report.pdf,这是预期提示,不是本文的实测记录。400先核文件是否确实叫index.html及表单字段;409结合日志查资源加载失败;503、客户端超时或连接错误先看服务状态和资源压力,停止交付本次结果,不连续无界重试。
这里没有远程图片、字体或页面脚本,先减少影响版式的外部条件。新增资源时,文件要一同上传并按相邻文件名引用;若资源缺失,启用的failOnResourceLoadingFailed可以让相应网络加载错误返回失败,但不会自动证明所有字体、文本和业务数据正确。
4. 分清网络地址与应用调用位置
在宿主机终端调用用127.0.0.1:3000。如果调用方以后也放到同一Compose网络,它访问的是服务名gotenberg:3000,而不是自己容器里的localhost。只有在实际添加应用服务并验证网络后,才使用该地址;本例没有附带应用容器,不需要为演示另拉一张工具镜像。
浏览器访问3000根路径也不会出现一个报表编辑后台。业务代码负责取数、模板转义和权限,Gotenberg负责转换;管理页面能否打开不是本任务的验收标准。
5. 验纸张、内容与重新转换
%PDF-只排除明显的非PDF响应,不能证明文件结构完整或内容正确。用PDF阅读器打开out/report.pdf,核对标题、日期、两行明细、合计20和2000.00;本模板预期一页,出现空白页、截列或乱码都要退回模板与字体检查。需要机器校验时,可另用已安装的PDF工具解析页数和文本,不能把文件非空当作通过。
再做一次服务重建后的使用检查:
docker compose up -d --force-recreate gotenberg || exit 1
curl -fS --connect-timeout 5 --max-time 10 \
--write-out '\nHTTP %{http_code}\n' \
http://127.0.0.1:3000/health || exit 1
bash convert.sh
等健康接口恢复200后再执行脚本;失败则先回到状态和日志。转换后仍打开新文件核内容,不以两份PDF的字节完全相同为目标,生成时间等元数据可能变化。这个演示服务不保存报表成品,index.html、convert.sh和out/report.pdf均留在发起请求的宿主目录。
结束演示可执行:
docker compose down
该命令移除本演示的容器与默认网络,不删除宿主目录里的模板、脚本和PDF;需要清理文件时另按实际目录处理。没有数据库或业务状态需要挂载,不要为了“持久化”把整个宿主目录暴露给转换容器。
真实中文和多页明细应独立准备样本:检查字体是否存在、表格长字段是否换行、分页后是否漏行,必要时调整打印CSS后重转。对同一输入逐项复核,不把英文一页演示外推成复杂模板已适配,也不声称生成PDF就满足档案管理要求。
本文依据2026年10月27日核验的官方资料整理,为参考配置和演示流程;未在本机启动Docker、拉取镜像或执行转换,所有预期提示均非实测。