直接读取 pdf 文件体易失败,因 gin 缓存 c.request.body 一次后再次读取为空;应于 handler 开头立即读取并保存局部变量,或提前调用 parsemultipartform。

为什么用 gin.Context 直接读取 PDF 文件体容易失败
因为 PDF 是二进制数据,c.Request.Body 默认会被 Gin 缓存一次(用于中间件或日志),第二次调用 io.ReadAll(c.Request.Body) 就会返回空字节。这不是 bug,是 Go 的 http.Request.Body 设计使然。
实操建议:
- 在路由 handler 开头立刻用
c.Request.Body读一次,并保存到局部变量,后续全部用这个变量 - 或者提前调用
c.Request.ParseMultipartForm(32 (如果走 form-data)再从 <code>c.FormFile("file")获取 - 更稳妥的做法:用
c.GetRawData()(Gin v1.9+),它内部已处理了 Body 复用问题,返回[]byte
如何用 gofpdf 在 PDF 上叠加透明印章图
gofpdf 本身不支持直接“盖章”,得把印章 PNG 转成 PDF 页面,再用 AddPageTemplate 或 ImportPage 合并。但更常用的是先用 image/png 解析印章图,提取 RGBA 数据,再用 gofpdf 的 ImageFromBytes 插入——注意 PNG 必须含 alpha 通道,否则白底无法透明。
关键点:
- 印章图推荐 300dpi、尺寸控制在 200×80px 左右,太大影响 PDF 渲染性能
- 插入位置用
SetXY(x, y)定位,y 坐标要换算:PDF 原点在左上,而多数印章需求在右下角,需用pdf.GetPageSize().Height - y_offset - 缩放印章用
ImageOptions{Width: 120, Height: 40, ReadDpi: true},避免拉伸失真
gin.H 返回 JSON 时中文乱码或字段丢失
Gin 默认用 Go 原生 json.Marshal,对非 UTF-8 字符串或含私有字段(首字母小写)的 struct 会静默忽略。常见于返回错误信息含中文,或结构体里用了 err string 这种字段名。
解决办法:
- 确保所有字符串字段都是 UTF-8 编码(Go 字符串默认就是,但若从第三方 API 读取,需检查
Content-Type: text/plain; charset=gbk类情况) - 结构体字段必须大写开头 +
json:tag,例如Msg string `json:"msg"`;小写字段不会被序列化 - 如需输出
nil字段,加omitempty的反面:用json:",string"强制转字符串,或改用map[string]interface{}动态构造
签名坐标和时间戳怎么做到“不可篡改”
接口层能做的只是生成带时间戳和坐标的 PDF,真正的防篡改靠数字签名(CMS/PKCS#7)或哈希上链。单纯在 PDF 里写个 signed_at: 2024-05-20T10:30:00Z 没有意义——文件内容随时可被编辑工具修改。
轻量级方案:
- 用
github.com/unidoc/unipdf/v3/creator对最终 PDF 执行Sign,需准备 PKCS#12 证书(.pfx)和密码 - 若无证书,至少计算整份 PDF 的 SHA256,连同时间戳、签名人 ID 一起存入数据库,后续校验时比对哈希值
- 注意:Gin 接口返回前必须完成 PDF 写入和哈希计算,不能只返回临时路径让前端自己下载再校验
印章位置一旦写死,就别指望靠前端传坐标来“防抵赖”——那个坐标字段完全可以被伪造。真正可信的坐标,应由服务端根据文档页数、内容特征(比如关键词定位)动态计算得出。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











