文章目录
- 1. 为什么这个场景值得你停下来读 5 分钟
- 2. 30 秒认识它:一张图看懂 Filee 的全貌
- 3. 总架构:五层结构、8 个项目,边界干净得令人舒适
- 4. 重难点一:把"格式"当节点、把"转换器"当边——Dijkstra 路由规划
- 4.1 它解决了一个什么难题?
- 4.2 关键代码:一条"边"能携带多少语义?
- 4.3 关键代码:Dijkstra 的核心循环
- 5. 重难点二:没有窗口也能"触发"——纯状态机手势识别
- 6. 重难点三:圆环不是一个控件,而是一段"数学"
- 7. 重难点四:三档引擎体系——"开箱即用"与"按需下载"的平衡术
- 7.1 引擎如何被"找到"?
- 7.2 下载引擎的安全与工程细节
- 7.3 令人"服气"的两个自研模块
- 7.4 其他值得一提的引擎能力(浓缩版)
- 8. 重难点五:交付的"最后一公里"——命名、原子性与"永不覆盖"
- 9. 把一次转换完整走一遍:流程图 + 时序图
- 10. 那些容易被忽略、却决定体验的"横切能力"
- 11. 工程治理:这是"产品"而不是"练习作品"的证据
- 12. 读到这里,你至少能带走 5 个可直接复用的设计模式
- 13. 结语:一个"小而美"工具背后的"大工程"
1. 为什么这个场景值得你停下来读 5 分钟
做工具的人都有一个共识:用户不是不会转换文件,是嫌麻烦。
转格式这件事,绝大多数人的路径是这样的:打开一个转换网站 → 上传 → 等 → 下载 → 清理隐私顾虑;或者打开一个笨重软件 → 新建任务 → 选源、选目标、选目录 → 点确认。每一步都在把你的注意力从"事情本身"拽走。
Filee 的答案很"暴力":把转换入口压缩成一次拖拽。你不需要先打开它,它在你的光标旁边出现——而且只显示适合当前这些文件的格式,与本次转换无关的一律不出现。
更关键的三点,是它能成为"爆款"的底层原因:
- 270 种格式,绝大多数无需额外软件——图片、PDF、Word、Excel、PowerPoint、HWP(韩文办公文档)、电子书、压缩包、字体、CAD、3D 模型、地理信息数据等,不需要 Office、不需要 HWP 办公套件、不需要 LibreOffice 就能转。
- 完全本地、离线、不上传、无遥测——转换在你自己的电脑上完成,结果保存在原文件旁边。这是隐私敏感人群的刚需。
- 免费且开源,MIT 许可证——由独立开发者发布与维护,Windows 版本带有代码签名证书,仓库多次登顶平台榜单、被知名开源推荐栏目收录。
本文不是"怎么装怎么用"的说明书,而是扒开源码看它是怎么设计出来的。我会把那些"看起来简单、做起来要命"的重难点挑出来讲,并给出可直接复用的关键代码。这也是这篇内容值回票价的地方。
2. 30 秒认识它:一张图看懂 Filee 的全貌
图 1 · 功能全景图:入口方式、18 大格式家族、三档引擎体系、以及"安全·隐私·落地"四条底线。
它支持的"入场姿势"远不止一种:
| 入口 | 说明 |
|---|---|
| 光标圆环拖拽 | 按住 Ctrl(macOS 为 Option)拖文件,圆环在光标处弹出 |
| 浏览器直接拖 | 从 Chrome / Edge / Firefox / Safari 把图片、文件链接、下载项拖出来即可 |
| 资源管理器右键菜单 | 右键文件即可转换(Windows 会主动集成到主菜单) |
| 键盘快捷键 | 全局快捷键触发,作用域可配置 |
| 主页拖放区域 | 打开主窗口后的拖放区 |
filee命令行 | 与应用同一套引擎与预设 |
| 监视文件夹 | 放进目录就自动转换 |
而"格式家族"覆盖之广,是它和普通转换器最本质的差距(共 270 种):
| 家族 | 代表格式 | 家族 | 代表格式 |
|---|---|---|---|
| 图片 | JPG/PNG/WEBP/AVIF/HEIC/PSD/相机 RAW/DICOM/FITS | 矢量图 | SVG/SVGZ/EMF/WMF/AI/EPS/PS/CDR |
| 转 DOCX/HWPX/TXT/图片/PPTX/CBZ,合并、拆分、压缩 | 文档 | DOCX/DOCM/DOTX/ODT/RTF/WPS/OFD/Outlook MSG | |
| 表格 | XLSX/XLSM/XLS/ODS/CSV/TSV,也可转 JSON | 演示文稿 | PPTX/PPTM/PPSX/ODP/Keynote |
| HWP | HWP ↔ HWPX → PDF/DOCX/TXT/Markdown/HTML | 文本 | TXT/源码/Markdown/HTML/reStructuredText/LaTeX |
| 电子书 | EPUB/KEPUB/MOBI/AZW3/FB2/CBZ/CBR | 视频 | MP4/MOV/MKV/WEBM/AVI/GIF/APNG |
| 音频 | MP3/M4A/FLAC/OGG/OPUS/DSD(DSF、DFF) | 字幕 | SRT/ASS/WebVTT/SAMI/LRC/TTML/MicroDVD |
| 压缩包 | ZIP/7Z/RAR/TAR/ISO/CAB/DMG/ALZ/EGG | CAD | DWG ↔ DXF → PDF/SVG/图片 |
| 字体 | TTF/OTF/WOFF/WOFF2/EOT/FON | 数据 | JSON/YAML/TOML/XML/vCard/iCalendar ↔ CSV |
| 3D 模型 | GLB/glTF/STL/OBJ/3MF/PLY/FBX | 地理信息 | Shapefile/GeoJSON/KML/KMZ/GPX/Garmin FIT |
提示:上表中未标注的格式开箱即用;标注的少数格式需要可选引擎(如 EPS/PS 需要 Ghostscript,旧版 DOC/PPT 需要 LibreOffice,Markdown↔DOCX 需要 Pandoc,rare 电子书需要 calibre,音视频需要 FFmpeg)。这套"按需下载"的引擎体系,是后文的一个重点。
3. 总架构:五层结构、8 个项目,边界干净得令人舒适
先看"运行时"是怎么分层的——这是理解整个项目的钥匙。
图 3 · 运行时分层架构:一次拖拽转换,数据自上而下穿过"触发层 → 应用层 → 核心层 → 引擎层 → 平台层"。
再看代码工程是怎么组织的(Filee.slnx里的 8 个src项目 + 6 个测试项目):
图 2 · 工程模块架构:上层依赖下层;
Filee.Core不碰 UI、不碰 OS,是整套系统的"纯净心脏"。
这三条设计纪律,值得每个做桌面端的人抄作业:
Filee.Core是纯领域层——路由规划、格式注册、预设、输出命名、设置、监视全在这里,没有任何 UI 代码、没有任何 OS 调用。这意味着核心逻辑可以被 CLI、GUI、安装器复用,也可以被单元测试直接覆盖。Filee.Engines只做"转换"一件事——所有引擎(内置 / 捆绑 / 可选)都在这里,IConverter是唯一契约。- 所有 OS 差异都从
IPlatformServices走——Windows / macOS / Linux 各自一个平台项目,全局输入统一用跨平台钩子库。
一句话总结它的数据流向:
全局鼠标/键盘 ──► TriggerService ──► GestureDetector ──► RadialController ──► RadialWindow(透明置顶圆环) Explorer 右键菜单 ──► SingleInstance(管道)──► --convert files ──►┘ 主页拖放区 ─────────────────────────────────────────────────────►┘ ▼ ConversionService ──► ToastWindow(进度) MainWindow(设置页)◄──► UserDataStore(%APPDATA%\Filee 下的 JSON) ▼ JobQueue ──► RoutePlanner(在转换器边上跑 Dijkstra)──► IConverter 步骤 ──► 输出命名4. 重难点一:把"格式"当节点、把"转换器"当边——Dijkstra 路由规划
这是整个项目最漂亮的设计,也是它"270 种格式"的真正来源。
4.1 它解决了一个什么难题?
如果每种格式转换都写死一条路径,N种源格式 ×M种目标格式就是N×M的爆炸。Filee 不这么做:引擎只声明自己会的"直接转换"(边),路由规划自动把多条边串起来。
举几个真实例子:
HWP → PDF:rhwp一步搞定;HWPX → PNG:HWPX →(rhwp)→ PDF →(PDFium)→ PNG,两跳;Markdown → PDF:Markdown →(自研 DOCX 写入器)→ DOCX →(rhwp)→ PDF;XLSX → CSV → JSON、vCard → CSV → JSON……都靠边自动组合。
图 6 · 路由规划:格式是节点,转换器是带权边;Dijkstra 搜索最省路径,最多 3 跳,引擎优先级用来打破平局。
4.2 关键代码:一条"边"能携带多少语义?
ConversionEdge用 C# 的record struct定义,短短几行,却把边的语义表达得清清楚楚:
publicreadonlyrecordstructConversionEdge(stringFrom,stringTo,intCost=10,boolImageSourcesOnly=false,// 仅当起点是"位图"才走(描摹专用)boolNotForImageSources=false,// 位图起点永远不走(PDF→SVG 要保矢量)boolDirectOnly=false,// 只能作为独立路由(PDF→PPTX 图片页)boolFrameSequence=false,// 视频逐帧导出boolUnshaped=false,// 该引擎不做复杂文本整形boolMotionPhoto=false,boolLivePhoto=false,boolOcr=false,boolCaption=false);这几个布尔标记,几乎每一行都是被真实需求"逼"出来的:
ImageSourcesOnly:矢量描摹(位图 → SVG)只能从位图出发,绝不允许把 PDF、矢量图、视频帧渲染成 PNG 后被"描摹";NotForImageSources:反过来,PDF → SVG(走 LibreOffice)保留矢量,但一张照片变成 PDF 后再转 SVG 只会变成一张位图,所以位图起点禁走;DirectOnly:PDF → PPTX