项目地址: filebox.fanyang.me
项目背景
起因很简单——想把本地已经安装好的 Ghostscript 做成一个可以通过浏览器访问的在线工具,方便自己和他人压缩 PDF 文件,后期计划扩展到图片、视频、文本等多种文件格式的压缩处理。
技术选型
| 层级 | 技术 |
|---|---|
| 压缩引擎 | Ghostscript |
| 后端框架 | Python + FastAPI |
| 前端 | 原生 HTML / CSS / JavaScript |
| 服务器 | Ubuntu + LNMP(Nginx 反向代理) |
| HTTPS | Let’s Encrypt(LNMP 内置工具申请) |
选择 FastAPI 的原因是它原生支持异步处理,对于 I/O 密集型的文件压缩任务性能较好,且部署简单。前端没有使用任何框架,保持轻量。
功能列表
核心功能
- 拖拽或点击上传 PDF 文件
- 四档压缩质量选择:极限(72 dpi)、均衡(150 dpi)、高清(300 dpi)、印刷级(300+ dpi)
- 支持批量上传,每次最多 10 个文件,单文件最大 50 MB
- 压缩完成后显示压缩前后文件大小对比及压缩率
- 即时下载压缩结果
安全与隐私
- 速率限制:每个 IP 每 60 秒最多发送 20 次请求,防止刷接口
- 文件内容校验:检查文件头魔数(
%PDF),防止伪装成 PDF 的恶意文件上传 - 自动清理:后台任务每 10 分钟扫描一次,压缩文件超过 1 小时自动删除,原始上传文件压缩完成后立即删除
- 手动删除:用户可在下载后主动点击删除按钮,立即清除服务器上的文件
- 路径过滤:对文件 ID 做字符过滤,防止路径穿越攻击
- Ghostscript 超时保护:单个压缩任务最长执行 300 秒,防止异常 PDF 卡死进程
并发处理
使用 asyncio.to_thread() 将同步的 Ghostscript 调用放入线程池执行,多个压缩请求可以真正并发处理,不互相阻塞。批量上传的多个文件通过 asyncio.gather() 并发压缩。
国际化
- 中英文双语界面,右上角一键切换
- 所有文案(界面文字、错误提示、按钮、结果标签)均支持双语
合规
- GDPR 合规:服务器位于欧盟境内(德国),数据不出欧盟
- 页面顶部展示数据处理告知横幅
- 完整隐私声明弹窗(中英双语),包含数据控制者说明、处理目的与法律依据(GDPR 第 6(1)(b) 条)、数据保留期限、用户权利(删除权、访问权、投诉权)等
- 监管机构:爱尔兰数据保护委员会(DPC)
其他细节
- Beta 阶段标注(顶部徽章 + 横幅说明)
- 反馈邮箱(混淆处理防爬虫,用户点击正常打开邮件客户端)
- 页脚个人网站链接
前端设计风格
极简科技风。白色背景、大量留白、精准的几何线条,使用 IBM Plex Sans + IBM Plex Mono 字体组合,蓝色(#0066ff)作为主色调。整体风格参考工程师审美,避免通用的 AI 设计模板感。
部署架构
用户(HTTPS)
↓
Nginx(LNMP,443 端口)
↓ 反向代理
uvicorn(127.0.0.1:8000)
↓
FastAPI + Ghostscript
WordPress 和 Filebox 运行在同一台服务器上,通过不同域名(fanyang.me 和 filebox.fanyang.me)由 Nginx 分发,互不干扰。
Filebox 通过 systemd 管理,配置开机自启和自动重启。
遇到的问题与解决
问题 1:忘记在公网服务器上安装 Ghostscript
本地虚拟机已安装,上线后才发现公网服务器没装。
sudo apt update && sudo apt install ghostscript -y
公网服务器(Ubuntu 22.04)安装的是 9.55.0,比本地虚拟机(Ubuntu 24.04)的 10.02.1 版本低,但对于项目使用的基础压缩功能完全没有影响。
问题 2:LNMP 虚拟主机默认配置不是反向代理
LNMP vhost add 生成的配置是静态文件服务模式,需要手动将 HTTPS server 块替换为 proxy_pass 反向代理配置,同时保留 LNMP 已自动配置好的 SSL 证书路径和 HTTP→HTTPS 重定向。
问题 3:Ghostscript 是同步阻塞调用
FastAPI 是异步框架,但 subprocess.run() 是同步阻塞的。直接调用会导致多个请求排队等待。解决方案是用 asyncio.to_thread() 将其放入线程池:
async def compress_pdf_async(input_path, output_path, quality):
return await asyncio.to_thread(_run_ghostscript, input_path, output_path, quality)
项目文件结构
/opt/www/filebox/
├── main.py # FastAPI 后端
├── requirements.txt # Python 依赖
├── venv/ # Python 虚拟环境
├── static/
│ └── index.html # 前端页面
├── uploads/ # 临时上传目录(自动创建)
└── compressed/ # 压缩输出目录(自动创建)
后续计划
- 加入磁盘用量监控,防止服务器空间被耗尽
- 扩展支持图片压缩
- 扩展支持视频压缩
- 扩展支持文本文件处理
- 域名独立:目前作为
fanyang.me的二级域名运行,后期考虑注册独立域名filebox.xxx