乐于分享
好东西不私藏

OFD 转 PDF,打开浏览器就行

OFD 转 PDF,打开浏览器就行

效率工具 · 个人实践

OFD 转 PDF,打开浏览器就行

零安装、隐私安全、即传即转一个为国产版式文档而生的在线工具

OFD——我国自主研发的版式文档标准,正在电子发票、电子票据、电子证照等领域快速普及。但你一定遇到过这样的场景:收到一份 OFD 文件,电脑里却没有阅读器,打不开、看不了、转不出。今天介绍的这个在线工具,让这件事变得简单到只需三步

线上地址:http://59.110.20.164/


一、OFD 的「最后一公里」之痛

OFD(Open Fixed-layout Document)是国家版式文档标准,在政务、税务、财政等领域已经成为「国标」。但现实是:

😤  阅读器难找:Windows 自带的不支持,常见的 PDF 阅读器也打不开,得专门下载安装 OFD 阅读器。

📎  分享困难:发给同事、客户,对方未必装了阅读器,沟通成本陡增。

🖨️  打印归档不便:很多办公系统的打印、归档流程只认 PDF。

🔒  在线转换顾虑多:市面上的在线转换工具,文件上传后去哪了?会不会被留存?隐私没底。

于是就有了这个工具——一个轻量级的 OFD 在线预览与转 PDF 的 Web 应用。不装软件、不留文件、打开浏览器就能用。


二、给普通用户:三步搞定转换

普通用户

无论你是财务、行政、法务,还是偶尔收到 OFD 发票的打工人,使用流程非常直观:

1

打开网页浏览器访问 http://59.110.20.164/,无需注册、无需登录。

2

上传 OFD 文件直接把.ofd文件拖到页面中央的上传区,或者点击选择文件。支持最大 50MB。

3

预览 + 下载几秒后转换完成,页面直接在线预览 PDF(支持翻页、缩放、旋转),点击下载按钮即可保存,文件名与原始 OFD 一致。

💡 转换失败了怎么办?

如果文件损坏或格式不兼容导致转换失败,页面会显示明确的错误提示(不是一串看不懂的报错),并提供「一键重试」按钮。加密 OFD 暂不支持,后续版本会加入密码输入功能。

🔒 我的文件安全吗?

安全。文件仅用于本次转换,24 小时后自动删除,不做任何持久化存储。服务端不会保留你的文件内容,统计数据只记录转换次数,不涉及文件本身。


三、给开发者:技术架构拆解

开发人员

如果你是后端或前端工程师,可能更关心这个东西是怎么做出来的。下面从架构到关键实现逐一拆解。

3.1 整体技术栈

层级
技术选型
用途
前端框架
Vue 3 + Vite 5
响应式 UI 与构建
PDF 渲染
PDF.js (pdfjs-dist)
Canvas 渲染 PDF
后端框架
Spring Boot 2.7
REST API + 异步处理
反向代理
Nginx
静态资源 + API 代理
进程管理
systemd
服务托管 + 崩溃重启

3.2 架构分层

整个系统是一个经典的「上传 → 异步处理 → 轮询结果」模型,关键在于任务状态机的设计。

3.3 任务状态机:六个状态撑起整个生命周期

每个 OFD 文件上传后都会创建一个任务,经历以下状态流转:

UPLOADED → QUEUED → PROCESSING → SUCCEEDED                       ↓                    FAILED → (retry) → QUEUED    任何状态 → EXPIRED (24h 后自动清理)
状态
进度
含义
uploaded
0%
文件已上传并完成校验
queued
10%
任务入队,等待 Worker 消费
processing
30-40%
正在解析 OFD 并生成 PDF
succeeded
100%
转换完成,PDF 可预览/下载
failed
0%
转换失败,可重试
expired
临时文件已过期清理

3.4 后端关键设计点

异步队列处理。上传接口立即返回任务 ID,转换任务通过@Async提交到固定大小线程池,避免大文件转换阻塞 HTTP 请求。前端通过 1.5 秒间隔轮询任务状态,收到终态后停止。

自动清理机制。独立守护线程每 5 分钟扫描工作目录,删除超过 24 小时的文件目录,同时将内存中对应任务标记为EXPIRED。这是隐私优先原则的技术保障。

统计持久化。使用AtomicLong线程安全计数,每次转换成功原子递增,同时写入stats.json文件持久化。服务重启后从文件恢复,不会丢失历史数据。

3.5 前端关键设计点

PDF.js 渲染的坑。这是整个前端最值得说的细节。PDF.js 默认使用 Range 请求和流式传输来分段加载 PDF,但在 Vite dev 代理和部分 Nginx 配置下,流式传输会被中断,导致ERR_ABORTED错误。解决方案是强制关闭这两个特性:

// 关键配置:强制完整下载 PDF getDocument({   url: previewUrl,   disableRange: true,   // 禁用 Range 请求   disableStream: true   // 禁用流式传输 })

多标签页统计同步。右上角的「累计成功转换次数」需要在多个浏览器标签页之间实时同步。方案是「轮询 + localStorage 广播」组合拳:每 3 秒轮询/stats接口,发现计数变化时写入 localStorage,其他标签页通过storage事件即时感知。即使 localStorage 不可用,轮询也能保证 3 秒内的最终一致性。

高 DPI 适配。PDF.js 的 Canvas 渲染需要处理devicePixelRatio,在高分屏上才能保持文字清晰。逻辑像素与物理像素的映射关系需要正确设置 Canvas 的width/height和 CSS 尺寸。

3.6 API 一览

接口
方法
说明
/api/ofd/tasks
POST
上传 OFD 并创建转换任务
/api/ofd/tasks/{id}
GET
查询任务状态与进度
/api/ofd/tasks/{id}/preview
GET
在线预览 PDF(inline)
/api/ofd/tasks/{id}/download
GET
下载 PDF(attachment)
/api/ofd/tasks/{id}/retry
POST
重试失败任务
/api/ofd/capabilities
GET
获取前端配置(大小限制等)
/api/ofd/stats
GET
获取成功转换统计

四、给产品经理:设计思路与产品哲学

产品经理

这个工具没有复杂的功能堆砌,但每一个设计决策背后都有清晰的产品逻辑。

4.1 核心设计原则

原则
落地方式
零安装
纯 Web 应用,浏览器打开即用,无需 OFD 阅读器或插件
隐私优先
文件 24h 自动删除,不做持久化存储,统计只记次数不记内容
异步处理
上传即返回,前端轮询,不阻塞请求,体验流畅
容错友好
明确错误码 + 友好提示 + 一键重试
生产可用
systemd 托管、Nginx 反代、崩溃自动重启、开机自启

4.2 产品决策背后的思考

为什么不做批量上传?首版关闭了批量功能(batch-enabled: false)。这是一个典型的 MVP 取舍——先验证单文件转换的核心链路是否稳定可靠,再考虑批量的交互复杂度。批量不是不能做,而是不该在核心路径未验证时就做。

为什么用内存存储而不是数据库?任务记录使用ConcurrentHashMap,服务重启会丢失。这听起来是个缺点,但在这个场景下反而是优势——文件本身就是临时的、24 小时过期,任务记录的生命周期跟文件一致,用数据库反而引入了不必要的持久化复杂度。后续如果需要水平扩展,再替换为 Redis 或数据库即可。

为什么展示累计转换次数?这个右上角的小数字,看似只是统计,实则是一个轻量的社会证明(Social Proof)设计。用户看到「已有 N 次成功转换」,会产生信任感——这工具是有人在用的、是可靠的。同时多标签页实时同步的体验,也间接展示了系统的实时性。

为什么预览和下载分开?预览用Content-Disposition: inline(浏览器内嵌显示),下载用attachment(触发保存)。用户可以先看再下,避免下了打不开的尴尬。文件名保留原始 OFD 文件名,减少用户的认知负担。

4.3 错误处理的产品化思维

很多工具的报错是「转换失败」四个字,用户一脸懵。这个工具定义了 8 种错误码,每种都有对应的触发场景和友好提示:

错误码
场景
用户感知
FILE_TYPE_INVALID
非 OFD 文件
「请上传 .ofd 格式的文件」
FILE_TOO_LARGE
超过 50MB
「文件过大,请压缩后重试」
OFD_PARSE_FAILED
文件损坏
「文件可能已损坏,请检查源文件」
UNSUPPORTED_FEATURE
加密 OFD
「暂不支持加密文件,后续版本支持」
CONVERT_TIMEOUT
超过 120 秒
「转换超时,请重试」
SERVICE_BUSY
队列满
「服务繁忙,请稍后重试」

这种「错误码 → 友好提示 → 可操作建议」的三层结构,是把技术细节翻译成用户语言的产品化实践。


五、部署方案:一个人也搞得定

开发人员产品经理

生产部署非常轻量,一台云服务器即可搞定:

互联网用户     │  HTTP :80     ▼   Nginx (静态资源 + /api 反代 → :8080)     │     ▼   Spring Boot JAR (-Xms256m -Xmx1g)     │     ▼   文件系统 (/opt/ofd-preview-pdf/data + logs)

几个关键配置点:

  • systemd 托管
    Restart=on-failure,崩溃后 5 秒自动重启,开机自启
  • Nginx 反代
    proxy_buffering off支持 PDF 流式读取,client_max_body_size 50m允许大文件上传
  • 静态资源缓存
    expires 7d+Cache-Control: immutable,减少重复请求
  • SPA 路由
    try_files $uri $uri/ /index.html,支持前端路由刷新不 404

六、已知限制与未来规划

坦诚地说,当前版本还有一些不足,但都有明确的演进方向:

当前限制
未来方向
内存存储,重启丢失任务记录
替换为 SQLite / Redis
单机部署,不支持水平扩展
引入分布式任务队列(RabbitMQ)
无认证,任何人可上传
增加速率限制 / Token 认证
默认 HTTP,无加密传输
配置 Let's Encrypt + HTTPS
不支持加密 OFD
集成密码输入 + 解密转换
不支持批量上传
多文件队列批量转换

立即体验

打开浏览器,上传 OFD,几秒拿到 PDF

http://59.110.20.164/

如果你也经常和 OFD 文件打交道,不妨收藏这个工具。也欢迎把它分享给身边还在为「打不开 OFD」发愁的同事。

— 技术栈:Vue 3 + Spring Boot  + PDF.js —