文档索引

格式转换器首轮实施记录

状态:首轮功能已交付,原计划的全部验收条件尚未满足(2026-10-09;实际交付见 §12.2)。 编写日期:2026-10-08;校对日期:2026-10-12(Asia/Shanghai)。 首版范围(图片 + 短音频)已超额交付为八个能力区(图片 / 音频 / 视频 / 文档 PDF / 表格 / 文本字幕 / 电子书 / 压缩包),全部在浏览器本地完成。子系统说明见 docs/architecture.md §6.17,玩家向说明见 docs/guide/格式转换器使用指南.md。 下文的首版范围、默认参数与技术路线保留为当时的实施建议(§7.1 的限额表已由 src/lib/file-converter/formats.ts 的 LIMITS 取代,仍属「起始值、非实测上限」)。 本文作为已交付功能的实施记录留在 docs 根目录;剩余验收与扩展统一登记在 docs/plan/format-converter-roadmap.md §16,不把「功能存在」当成 M0–M4 全部验收通过。

长期产品形态、八类文件的转换方向与具体处理路线,另见 docs/plan/format-converter-roadmap.md。§1–§11 保留图片与短音频首版的原始实施建议, 用于对照当时的范围、约束和验收要求;当前实现与偏差以 §12 及代码为准。

1. 目标与首版边界

1.1 用户要完成的事情

在工具箱中进入「格式转换器」,选择本地文件和目标格式,转换后下载到自己的设备。 首版优先解决日常图片与短音频的兼容问题,例如:

  • 把 JPG 图片转换为 PNG,或把带透明背景的 PNG 转成指定背景色的 JPG。
  • 把 WebP 图片转换成更容易被其他软件接受的 JPG / PNG。
  • 把 OGG 录音转换为 MP3,或把 WAV / FLAC / M4A 音频转换为 MP3。
  • 一次选择多个同类文件,逐个转换、逐个下载,看到各自的成功或失败结果。

文件格式由实际解码和重新编码改变。只改扩展名不能完成转换。

1.2 首版必须交付

项目 首版要求
入口 工具箱中默认可见的「格式转换器」卡片,站内路径 /tool/convert
分类 「图片」「音频」两个标签页,分别展示支持范围
文件选择 点击选择与桌面拖放,手机保留完整的点击选择流程
图片转换 静态 JPEG / PNG / WebP 输入,PNG / JPEG 输出;WebP 输出按设备能力提供
音频转换 MP3 / WAV / FLAC / M4A / OGG 输入,MP3 / WAV 输出;具体编码组合按 M0 白名单验收
参数 图片质量、JPG 背景色;MP3 码率
反馈 识别、加载、排队、转换、成功、失败、取消等状态明确区分
结果 输出名称、格式、体积与图片预览 / 可用的音频试听,以及手动下载按钮
恢复 单项失败不阻断后续任务;允许取消、重试和释放结果
隐私 转换只在浏览器内进行,用户文件不发送到本站或第三方服务
集成 遵守本站样式规范、亮暗主题与文档登记规则

纯本地工具建议与现有编码、哈希工具一样匿名可用,不增加角色门槛、鱼干收费或 专注模式限制。这是本计划采用的产品默认值,不代表用户已确认所有产品细节。

1.3 首版范围之外

视频转码、视频提取音频、动画格式互转、HEIC / TIFF / RAW、SVG、PDF / Office 文档、 压缩包转换、裁剪、剪辑、降噪和文件修复均属于后续扩展。

首版也不提供任意 FFmpeg 命令、URL 导入、自动上传图床 / 音频床、云端任务历史、 跨设备继续任务和一键 ZIP 打包。批量任务按同一媒体类别建立,混入其他类别时逐项 说明原因,不按扩展名猜测后强行转换。

2. 本站现状与接入约束

2.1 已核对的现状

落点 现状 对实施的影响
src/app/tool/ToolMenu.tsx 工具清单、搜索、分类与卡片集中在同一文件;现有分类为站务、编码、加密 新入口和分类在这里增加,保持站内链接
src/app/tool/hash/page.tsx 已有纯前端工具与本地文件处理 转换器可以沿用浏览器处理的产品口径
package.json 已有 sharp,没有 FFmpeg.wasm / Mediabunny 图片原生路线无需新增图片库;音频需要新增依赖
src/lib/audio-upload.ts 音频床刻意只保存原字节,未引入服务端 FFmpeg 转换器独立完成转换,保持音频床现有上传契约
next.config.mjs 中间件请求体上限配置为 12mb 浏览器转换不走上传链路,不需要提高该值
docs/deploy.md §6 nginx 上传请求体上限配为 12m 若以后增加服务端转换,必须重新设计上传边界
src/app/components/FrameBuster.tsx 本站刻意允许第三方 iframe 嵌入 首版不能依赖嵌入方配合提供跨源隔离
src/lib/docs-catalog.ts 每份项目文档都要登记,标题须与 H1 一致 本计划及未来使用指南均按同一登记规则加入

本站仍以现有 Next 应用作为部署单元。首版不需要新服务、数据库表、迁移、后台循环 或用户文件存储目录。权限与存储背景见 docs/architecture.md §6.6/§8,音频床见 docs/architecture.md §6.15。

2.2 工具箱入口安排

建议增加「文件处理」分类,卡片放在工具箱默认展开区域、位于站务工具之后。 格式转换是日常用途,入口不放进「更多开发者工具」折叠区。

  • 卡片标题:格式转换器。
  • 描述:图片与音频格式转换,文件在本机处理。
  • 搜索词包含:格式、转换、文件、图片、音频、JPG、JPEG、PNG、WebP、OGG、MP3、WAV、FLAC、M4A。
  • 标签:图片、音频、本地处理。
  • 首版卡片使用站内路径,不链接到第三方转换站。
  • 「全部」「文件处理」筛选和搜索均可找到入口;无匹配结果时不留下空分组。

菜单当前对默认展开部分和折叠部分的渲染方式不同,实施时要同时调整分类词表、 分组数据和实际渲染,不能只往数组加一项。保留既有工具入口的可达性测试。

3. 支持格式与转换结果的契约

3.1 图片矩阵

实际输入 PNG 输出 JPEG 输出 WebP 输出 处理规则
静态 JPEG(.jpg / .jpeg) 必须 必须 能力探测通过后提供 正确应用 EXIF 方向,默认保持像素尺寸
静态 PNG 必须 必须 能力探测通过后提供 PNG 输出保留透明;JPEG 输出先铺指定背景色
静态 WebP 解码成功时必须 解码成功时必须 能力探测通过后提供 识别动画标记,动画文件拒绝进入静态流程
APNG / 动画 WebP 不提供 不提供 不提供 明确提示首版不支持动画,不能静默只保留一帧
GIF、BMP、HEIC、TIFF、SVG、其他格式 不提供 不提供 不提供 显示不支持的格式,扩展时另加解码器与验收样本

浏览器原生解码失败时不得伪装成功。WebP 输出能力在运行时以小图试编码,并核对 返回 Blob 的 MIME / 字节;不支持则不给出可执行的 WebP 输出选项。 Canvas 请求不支持的类型时会回退到 PNG,这是必须显式防止的错误。 依据:Canvas toBlob 文档

输入同目标格式时按钮写「重新编码」,不承诺原字节不变。输出统一使用 .jpg、 .png、.webp 规范扩展名,并与实际字节和 MIME 一致。

3.2 音频矩阵

文件类别 首版纳入的内容 必须验证的目标 拒绝或提示的情形
MP3 常见 MPEG 音频,含 CBR / VBR 样本 MP3、WAV 无音轨、损坏、无法获得可信时长
WAV 常见 PCM 16 / 24 位与浮点 WAV 样本 MP3、WAV 不在实测白名单内的压缩 WAV
FLAC 常见单声道 / 双声道 FLAC MP3、WAV 超出声道、采样率或时长限制
M4A AAC in MP4 MP3、WAV ALAC、受保护内容或其他编码留到后续验证
OGG Vorbis / Opus 两种分别验收 MP3、WAV Theora 视频、多路音轨、首版未纳入的 Ogg 编码

容器支持不等于里面所有编码都支持。 检查以文件内容、媒体探测结果和实测白名单 为准;文件名与浏览器声明 MIME 只作提示。只含音频和附属封面图的文件可接受, 封面不导出;含正常视频轨、多个音频轨或无法识别的轨道时明确拒绝。

首版有意缩小输出集合为 MP3 / WAV。OGG、FLAC、M4A 输出按后续需求逐个增加, 每增加一项,都要写明容器、编码器、参数与设备播放兼容性,不只增加一个后缀选项。

3.3 默认参数与保真边界

参数 建议默认值 首版允许调整
图片尺寸 保持经方向归一后的像素尺寸 不提供缩放
JPEG / 有损 WebP 质量 90% 50%–100%,整数滑块;这是编码参数,不是保真百分比
JPEG 背景色 白色 白色、黑色、自定义不透明颜色
PNG 浏览器原生无损像素编码 不提供无意义的「质量」滑块
MP3 码率 192 kbps 128 / 192 / 256 / 320 kbps
MP3 输出采样率 44.1 kHz 首版固定,重采样时明确说明
音频声道 保持单声道 / 双声道 多声道首版拒绝,不静默混缩
WAV 输出 16 位 PCM,保留已支持的输入采样率 首版固定;24 位 / 浮点输入要提示精度变化
元数据 不复制原始 EXIF、音频标签与封面 不提供保留开关

结果页需要说明:JPG 转 PNG 不能恢复已经丢失的细节;转成 PNG / WAV 后文件可能变大; 有损音频再次转码会损失音质;转成 WAV 不会提升原录音质量。WAV 输出的「无压缩」 不等于任意输入到输出都逐样本无损,例如 24 位输入到 16 位输出会改变精度。

首版面向普通网页图片与短音频,不承诺保留专业摄影色彩管理、ICC、HDR、相机 RAW、 打印 DPI、歌词与封面。EXIF 方向要正确,浏览器解码已应用方向时不能再重复旋转。 Canvas 输出也不承诺保留原文件的 DPI 元数据。

4. 技术路线与 M0 选型判据

4.1 建议基线

图片采用浏览器原生解码与 Canvas 编码;音频采用 FFmpeg.wasm 单线程核心。 转换器页面按需加载音频适配层和核心资源,图片流程不会触发音频库下载。

FFmpeg.wasm 在 Worker 内处理媒体,并提供单线程与多线程核心;官方示例将完整核心 标为约 31 MB。这里的数值只是上游示例,实际选用产物的未压缩体积、压缩传输量、 冷启动耗时与缓存命中要在 M0 分开测量。 依据:架构介绍、核心加载示例

4.2 三条路线的取舍

路线 文件在哪处理 优点 本计划的取舍
原生图片 API + FFmpeg.wasm 用户浏览器 编码能力较独立,统一处理常见音频;本站无需计算服务 首版基线,接受按需加载的大核心
原生图片 API + Mediabunny / 专用 WASM 用户浏览器 可只引入所需组件,传输量有机会显著降低 M0 做有界比较,后续可替换音频适配层
sharp + 原生 FFmpeg 本站服务器 原生处理性能更好,用户设备负担小 后续独立方案,需要上传、并发、超时与二进制运维设计

Mediabunny 提供容器读写和转换能力,但部分编解码依赖浏览器。其 MP3 扩展使用 LAME WASM,上游标注约 130 kB gzip;这个数只属于编码器,不包括容器解析、 解码器与页面代码,不能直接和 FFmpeg 的完整核心体积作同比。 依据:支持范围、MP3 扩展说明

4.3 M0 必须回答的问题

  1. 锁定一组互相兼容的 FFmpeg wrapper / core 版本,实际产物包含哪些解码器、编码器 与媒体探测能力?对 §3.2 的每个输入组合给出通过或失败样本。
  2. 单线程核心在桌面 Chromium / Firefox / Safari 与真实 iPhone Safari 上是否可加载、 完成 OGG Vorbis / Opus → MP3、取消后继续工作?iframe 场景是否也能完成?
  3. Next 开发、生产构建和 next start 中,wrapper Worker、核心 JS、WASM 的路径是否 都正确?从其他工具经客户端导航进入时是否仍能加载?
  4. 原生图片流程是否正确处理透明、EXIF 方向、静态 WebP 和动画拒绝?所选设备的 WebP 编码能力探测是否与实际输出一致?
  5. 在相同样本和设备上,Mediabunny 路线是否更轻且完整覆盖必需音频组合? 如果依赖不具备的浏览器解码能力,则保留 FFmpeg 基线,不引入两套自动回退引擎。
  6. §7 的初始限制是否合理?完整核心加载后的常驻内存、成功后的残留内存、取消后 的恢复行为,以及重复转换是否有持续增长?

M0 的产物是兼容性与测量记录,不是「所有格式都支持」的声明。替换基线引擎的 条件是:必需组合与取消恢复均通过,实际总传输量更低,内存与处理时间没有影响 目标设备的明显退化;在记录中说明选择原因并更新本计划。

M0 不通过时,图片可以独立进入发布流程,音频保留在开发阶段。此时必须把交付 标为「仅图片阶段」,不能在卡片或完成记录中宣称图片+音频首版已经完成。

4.4 iframe 与部署边界

多线程 FFmpeg 需要 SharedArrayBuffer;其使用受跨源隔离和权限条件约束。 第三方父页面不一定配合这些条件,因此首版固定单线程,不新增全站 COOP / COEP, 也不新增 X-Frame-Options / CSP frame-ancestors。 依据:FFmpeg 多线程要求、浏览器跨源隔离规则

嵌入方的 sandbox 仍可能禁止脚本、Worker 或下载。工具不能绕过父页面设置; 条件不足时显示具体原因和现有可用的完整页面入口。iframe 内的工具不依赖会话 cookie。 HTTPS 与 localhost 是重点验收环境,其他访问环境以实际能力检测提供明确提示。

5. 页面与交互设计

5.1 页面结构

桌面从上到下呈现:标题与本地处理说明、图片 / 音频标签、文件选择区、目标格式和 参数、任务列表、结果预览。较宽视口可以将参数区和任务区分列;手机使用单列, 文件名可以换行,主要操作始终可触达,页面不横向溢出。

默认进入图片标签,用户可主动切换音频。仅切换标签不下载音频核心,用户开始首个 有效音频任务时才触发加载,并提示首次需要下载转换组件。页面不自动扫描、打开 或上传用户的其他文件。

用户文案示例:

  • 文件只在你的浏览器中处理,不会上传。
  • 首次转换音频需要下载转换组件,之后通常可复用浏览器缓存。
  • 页面关闭后,尚未保存的结果会丢失。

5.2 操作与参数冻结

  1. 选择文件后逐项校验类别、大小与内容,不自动开始转换。图片可以读取尺寸与动画 标记;音频先做轻量类型初筛,完整音轨 / 时长探测在开始执行后进行,避免提前下载核心。
  2. 用户选择目标与参数,任务列出预计要执行的转换。
  3. 点击「开始转换」时为本批任务保存参数快照,按顺序执行。
  4. 转换期间可取消当前任务或整批任务;成功结果保留。
  5. 修改全局参数只影响下一批或重试生成的新任务,不改变已经成功或正在执行的任务。
  6. 用户手动试听 / 下载结果。重试必须显式说明沿用旧参数还是使用当前参数。

批量不触发多个自动下载,避免浏览器拦截连续下载。首版每个结果都有独立下载按钮。 处理期间切换类别不丢队列,两个类别共用一个执行调度器。

5.3 结果与可访问性

  • 原文件名和输出名使用普通文本渲染,结果名形如 录音-converted.mp3;重复名称 加序号,不能把文件名当 HTML,也不能把原名直接作为引擎内部路径。
  • 图片预览展示实际输出;透明图片使用棋盘背景,JPG 显示实际填充后的背景。
  • 音频使用受控的本地 Blob URL,手动播放;播放不支持和转换失败分别显示。
  • 原始体积、输出体积和增减比例来自实际文件,输出更大时照实显示。
  • 选择区有键盘可用的按钮;拖放不是唯一入口。状态通过适度的 aria-live 播报, 不在每次进度事件上打断屏幕阅读器。
  • 图片操作要能独立于音频核心失败而继续使用,失败项可被单独移除。

样式依据 docs/frontend-styles.md:复用已有按钮、字段、容器与主题令牌。 不引入另一套组件库;单行字段与多行内容采用各自规定的圆角与聚焦样式。

6. 前端模块与数据流

6.1 计划新增的模块

以下均为拟新增路径,当前尚不存在;实现时可以合并只承担少量职责的文件,避免 为了分层而增加转发模块。

src/app/tool/convert/
  page.tsx                         页面外壳、metadata、客户端入口
  ConverterApp.tsx                 标签页、参数、任务列表、结果
src/lib/file-converter/
  formats.ts                       格式词表、MIME、输出扩展名、参数与限制
  types.ts                         任务、结果、错误、引擎接口
  inspect.ts                       内容识别、尺寸 / 动画检查、输入初筛
  image.ts                         图片解码、方向、背景与 Canvas 编码
  audio.ts                         动态加载、探测、固定参数转码、资源回收
  queue.ts                         串行执行、取消、任务代次、结果预算
scripts/copy-converter-assets.mjs   从锁定依赖重建同源核心与许可资料
public/static/converter/           可重建的 JS / WASM / 许可说明产物
src/styles-scss/pages/_tool-convert.scss
tests/unit/file-converter-*.test.ts
tests/e2e/file-converter.spec.ts
tests/e2e/file-converter-layout.spec.ts

formats.ts 是零依赖词表;客户端模块不能 import 带 Prisma、sharp、Node fs 的上传 服务。可复用现有纯工具函数,但不能为了复用一个常量把服务端模块带进客户端包。

6.2 数据流

用户主动选择本地 File
  → 类型 / 大小初筛
  → 内容识别、尺寸 / 动画检查或音轨探测
  → 建立任务,冻结目标格式与参数
  → 共用队列(同一时间仅一个任务执行)
      ├─ 图片:本地解码 → 背景 / 方向 → Canvas 编码
      └─ 音频:按需加载 Worker → 写入临时文件 → 探测 / 转码
  → 校验实际输出格式、非空结果与资源预算
  → 本地 Blob → 预览 / 试听 → 用户手动下载
  → 释放不再使用的 URL、像素缓冲与引擎临时文件

这个流不调用图床、音频床或其他业务写接口。页面与核心的静态资源请求照常存在, 「不上传文件」不能表述成「页面没有网络请求」或「第一次访问就能离线使用」。

6.3 引擎接口与任务状态

两类引擎输出同一结构:输出 Blob、实际格式 / MIME、建议文件名、元信息与结果提示。 输入校验、引擎加载、媒体探测、转换和结果交付分别报告状态;引擎差异不泄露到菜单。

建议任务状态:

validating → queued → loading-engine / probing → converting
                                              ├─ succeeded
                                              ├─ failed
                                              └─ cancelled

验证失败直接进入 failed,队列取消直接进入 cancelled。引擎可复用时跳过加载状态, 失败重试创建新的执行代次。每个异步回调校验任务 id 和代次,取消或移除后的旧结果 不能覆盖新结果。输出预算不足时队列进入明确的暂停状态,等待用户释放已有结果。

取消音频任务使用能终止实际计算的机制,不只拒绝一个等待中的 Promise。 FFmpeg 的 terminate 会终止 Worker 和进行中的调用,之后须重新 load。 正常完成在 finally 中回收临时文件;Worker 已终止时接受其文件系统一起消失, 不能让清理失败覆盖任务的原始错误。 依据:FFmpeg API

图片原生操作未必能中断底层编码。首版取消时丢弃晚到结果并回收资源,只在旧操作 真正结束后调度下一项;若 M0 发现大图明显阻塞交互,再采用经目标设备验证的 Worker / OffscreenCanvas 路线,不能宣称一个取消按钮必然立即停止浏览器编码。

6.4 音频探测与输出校验

音频任务使用内部生成的文件名和固定参数数组:显式选定唯一音轨、去掉封面与原 元数据、选择白名单编码器与参数。禁止拼接 shell 命令或接受用户输入的引擎选项。

写入一个输入后先探测时长、音轨、编码、采样率与声道;有探测能力时复用所选 FFmpeg 版本的探测 API。没有可用探测能力的版本不能直接跳过时长与音轨检查, 应在 M0 更换版本或确定等价方案。异常探测结果要拒绝继续编码。

成功判据包含退出码成功、实际输出非空、MIME / 扩展名 / 字节一致,每个音频输出 还要经过媒体探测确认所选编码、声道与采样率。不能把「生成了一段字节」当成可用 文件;失败时不下载半成品。 进度百分比仅在能够可靠获得时长时展示为估计值,完成状态以实际执行和输出校验 为准。加载进度与转码进度分开,不编造精确剩余时间。

7. 文件限制与资源生命周期

7.1 待实测的起始限制

⚠️ 下表是首版(图片+音频)的原始范围。实施后限额已扩展到八个能力区 (视频 / 文档 / 表格 / 文本 / 电子书 / 压缩包),唯一权威是 src/lib/file-converter/formats.ts 的 LIMITS —— 页面提示与校验读的是它, 本表不再逐项维护(见 docs/architecture.md §6.17)。下面的理由与推导仍然有效。

以下数值是 M0 的初始实验条件,不是已测得的安全上限。统一放在词表模块, 页面提示与校验读同一份值。上线前可以依据测量下调,并在本节记录依据。限制作用于 一个页面实例;多个浏览器标签页分别占用设备资源,首版不协调跨标签页的任务。

项目 起始值 理由 / 处理
单张图片输入 20 MiB 控制读取与容器解析成本
图片总像素 1600 万像素 压缩文件很小也可能解码成巨量内存;与字节限制并用
图片单边 8192 像素 防止极端长图;仍必须满足总像素限制
单段音频输入 20 MiB 首版面向短录音与短音乐片段
音频时长 5 分钟 控制手机计算成本与 WAV 输出膨胀
音频声道 1 或 2 多声道不隐式降混
音频输入采样率 白名单内且最高 48 kHz 更高采样率后续按样本验证
等待 / 持有的任务数 最多 10 项 防止无限排队与结果堆积
持有输入大小总额 100 MiB 统计仍持有 File 的所有任务,跨两个类别计算
单个输出与保留输出总额 各最多 64 MiB 输出大于预算则不保留,不自动触发下载
执行并发 1 图片和音频共用,避免同时争抢内存
核心加载超时 60 秒 可重试;图片流程保持可用
单次音频探测超时 15 秒 损坏文件不能让队列无限等待
单次音频转码超时 120 秒 超时终止 Worker,后续可重新初始化

例如 1600 万像素的 RGBA 原始缓冲约 64 MB,尚未计入解码副本、Canvas 与输出。 5 分钟、48 kHz、双声道、16 位 PCM 数据约 57.6 MB,尚未计入输入和引擎内存。 这两项说明字节小不等于内存小,也说明限制仍需真实设备验收。

WAV 输出可在转码前依据时长、采样率和声道估算;结果仍要复核实际体积。结果总额 不足时暂停后续任务,提示先下载并移除已有结果。单项预计超过输出上限时提前拒绝, 实际超过上限时丢弃结果并报告原因,不能长期持有超额 Blob。

输出体积检查不能替代执行中内存约束;Canvas 与 MEMFS 可能在检查之前就已经分配 大缓冲。首版使用输入、像素、音频时长限制降低成本,并测试畸形文件;仍可能出现 浏览器内存不足或标签页关闭,这种情况下不承诺当前任务可恢复。

7.2 回收规则

  • 不为每个排队文件预先读取完整 ArrayBuffer,不预先解码所有图片或音频。
  • 每次只为执行中的任务建立必要缓冲。音频结果取出后及时释放 MEMFS 中的输入和输出。
  • 图片解码对象、Canvas、预览 URL 与音频 URL 在替换、移除、取消和组件卸载时回收。
  • 结果不能刚显示就撤销 Blob URL;用户仍需预览、试听和下载。
  • 移除任务只释放浏览器内的引用,不改变用户磁盘原文件,也不涉及站内业务记录删除。
  • 下载后不假定文件已经落盘成功;结果由用户主动移除或离开页面时释放。
  • 队列空闲后按 M0 确定的策略释放音频 Worker,重新使用时可以重新加载核心。
  • 首版不保存文件到 localStorage、IndexedDB、OPFS 或服务端,不实现任务断点续作。

性能计时采用浏览器的单调计时 API;本地任务不需要写入数据库时间。 若未来确有业务记录写库,则遵循本站时间口径,见 src/lib/db-time.ts。

8. 核心资源、许可与部署

8.1 依赖与静态资源

实施时只增加必要的音频依赖,锁定 wrapper / core 的明确版本,不跟随 CDN 的 latest。 修改 manifest 与 lockfile 后用 npm ci 验证可重建,不升级 Next 或其他无关依赖。 是否需要 @ffmpeg/util 由适配代码决定,本地 File 读取与同源资源不必为了示例而 额外引入不使用的工具包。

wrapper 由 Next 打包,音频核心 JS / WASM 从锁定依赖复制到同源目录。 复制脚本参考 scripts/copy-emoji-assets.mjs 的可重建资产方式,给核心资源使用带版本 或哈希的子目录,避免旧 JS 与新 WASM 混配;公共生成目录进 .gitignore,二进制 不作为手工维护文件提交。脚本校验来源版本、必需文件和清单,缺文件时明确失败。

官方安装说明建议自行托管 wrapper,因其会创建 Worker;不能把从 CDN 直接 import wrapper 的示例当作适合本站的交付方式。 依据:FFmpeg.wasm 安装说明

复制脚本接入现有资源准备流程,并给跳过安装脚本的构建提供显式准备命令。核心来源 依赖必须在实际生产安装策略下可用,不能只列为被生产安装省略的开发依赖。检查项 覆盖核心 JS、WASM、wrapper Worker 路径、MIME 和文件清单。版本路径可配置长期缓存, 核心资源不修改时不重复下载;实际缓存结果以浏览器和部署响应头测量为准。

8.2 许可资料

引入 npm 代码与可重建 WASM 资产时核对相应版本的许可证、第三方库和构建配置。 FFmpeg wrapper 的 MIT 许可不代表核心也是 MIT,核心包含 FFmpeg 和外部编解码库, 适用许可可能受构建配置影响。复制脚本生成对应的许可说明、上游来源、版本、 构建方式与源码获取信息,不能只复制 wrapper 的 LICENSE 来代表全部产物。 依据:FFmpeg.wasm 许可说明、FFmpeg 官方许可说明

测试媒体使用自己生成或有明确再分发许可的样本。第三方音乐、照片和录音不入库。 若采用 Mediabunny,也分别核对主库 / 扩展的 MPL-2.0 与 LAME 的 LGPL 说明, 并保留所选版本要求的资料;不由其中一个包的许可代替整个组合的许可。

8.3 部署与回退

发布前验证生产构建和实际反代:首次加载、缓存命中、Worker 与 WASM 请求成功, 只使用图片时没有音频资源请求。服务端计算、数据库和上传目录不参与转换。

出现音频加载故障时允许暂时关闭音频标签的可执行功能,同时保留图片转换与明确 状态说明;不能把文件悄悄切换为上传服务器处理。回退代码版本时,HTML、wrapper 与核心资源版本一起回退,并验证旧资源路径仍满足部署策略。

9. 错误处理与隐私验收

9.1 错误类别

情形 用户应看到的结果 恢复方式
空文件、类别不支持 具体文件无法进入当前转换流程 移除或换文件
后缀 / MIME 与内容不一致 以内容为准识别;不符合白名单则拒绝 换正确文件,不靠改名通过
动画图片 首版不支持保留动画,未进行转换 使用其他工具或等待扩展
超过大小 / 像素 / 时长 显示对应上限与实际值(可得时) 缩小源文件或换较短文件
核心加载失败 转换组件未能加载;图片仍可用 用户主动重试
浏览器能力不足 缺少具体能力或格式支持 换支持的格式 / 浏览器 / 完整页面
损坏文件或不支持的编码 无法解码,并显示格式层面的原因 不重试同一失败参数直到无限循环
转码超时 / 取消 超时或已取消,未提供半成品 重试,重新加载已终止的引擎
输出预算不足 队列暂停,已有成功结果保留 下载并释放已有结果后继续
输出试听失败 转换结果已生成,本浏览器不能试听 允许下载;不要改判为转码失败

底层日志用于本地诊断,保留有界数量,避免完整控制台内容铺进产品界面。 错误映射失败时提供简明的通用错误和本地诊断信息,不假装已经判断出具体损坏原因。

9.2 隐私与内容边界

验收时拦截页面请求,确认没有用户媒体字节、文件名、元数据、预览和结果被发送到 转换 API、业务上传接口、分析服务或第三方域名。静态资源访问与站点已有请求要与 用户文件数据区分;不宣称浏览器本身或嵌入方不会进行其自己的行为。

只接受用户主动选择的 File。首版禁止远端 URL 输入和任意协议资源引用;不通过 innerHTML、iframe 或 object 标签呈现用户文件。SVG / HTML 不进入可接受媒体集合。 文件内容必须经实际解码,畸形或假冒文件不能只凭后缀获得成功结果。

10. 验证方案与发布验收

10.1 样本与分层验证

样本必须有独立可核对的预期,不能只断言「存在下载按钮」或「输出体积大于零」。 可入库的小样本使用自制图案与合成音频;大文件和压力样本按需本地生成。

层级 要覆盖的问题
纯模块单测 类型词表、参数边界、内容识别、动画标记、体积预算、文件名、取消后旧回调失效
图片真实浏览器用例 JPG → PNG 的尺寸与像素;PNG → JPG 的背景与容许色差;EXIF 方向;WebP 输出探测与字节;动画拒绝
音频真实引擎用例 OGG Vorbis / Opus 分别转 MP3;WAV、FLAC、AAC M4A 输入;输出编码、时长、采样率、声道与可解码性
队列与异常用例 成功 / 失败混合、串行、取消与重试、加载失败、超时、预算暂停、切换标签与卸载
资源与隐私用例 图片不加载音频核心;生产资源可达;无媒体上传;结果与临时文件释放
页面与布局用例 工具箱默认入口、搜索 / 分类、直接打开、客户端导航、亮暗主题、窄屏与键盘流程

MP3 输出是有损且可能有编码延迟,不按文件哈希或逐字节相等验收。使用合成音调和 已知声道,解码输出后检查时长容差、主频、非静音、声道和采样率;WAV 按声明的 PCM 规格验收,图片按格式和允许的色差验收。音频容差在 M0 记录并固定。

真实引擎用例必须使用将发布的同源核心,不能全部 mock 转码函数。独立解码 / 检查 输出可在开发测试环境使用测试工具,但不能因此把服务端 FFmpeg 加进生产依赖。

10.2 浏览器与设备矩阵

环境 必测事项 记录要求
桌面 Chrome / Edge 完整图片+音频流程、取消恢复、冷 / 热加载 浏览器版本、OS、硬件与样本
桌面 Firefox Worker / WASM、原生图片能力、音频完整流程 同上;记录能力差异
macOS Safari 下载、原生图片输出、音频与缓存 真机 / 实际 Safari 版本
真实 iPhone Safari 短音频、上限附近样本、结果试听 / 下载、取消与重复转换 iPhone 型号、iOS / Safari 版本
Android Chrome 文件选择、图片与短音频、下载与内存表现 Android 版本、设备型号
同源 iframe 全流程、下载和 Worker 嵌入页设置
跨源 iframe 普通嵌入下的全流程;受限 sandbox 下的原因提示 父页面 origin、权限和 sandbox 属性

仓库 Playwright 的 mobile 项目使用设备配置模拟视口,不能替代真实 iOS Safari。 新增布局 spec 要登记到 playwright.config.ts 的 RESPONSIVE_SPECS;桌面功能用例 按既有项目执行。是否加 WebKit 自动化项目按环境支持决定,仍不能代替 iPhone 实测。

10.3 性能测量与发布条件

每个重点设备至少记录一张典型图片、一张像素上限附近图片、两种 OGG 编码的短样本、 一个时长上限附近音频,以及连续十次小文件转换和取消后重试。

分别记录:图片页新增 JS、音频核心原始 / 压缩传输量、冷加载 / 热加载、探测与转码 耗时、输出大小、UI 响应和可获取的内存指标。Safari 无法取得一致的 JS 内存数字时, 记录系统工具观察和浏览器是否重载;不能编造与 Chromium 可直接比较的数值。 上游视频基准只说明 WASM 与原生性能有差距,不能拿来当本站音频处理时间。 依据:上游性能说明

发布的必要条件:

  • §3 中的必需输入 / 输出组合在声明支持的设备上通过,文案与实测白名单一致。
  • 必测样本没有可复现的崩溃、错误格式输出或取消后任务串扰。
  • 正常输入在设定超时内完成;阈值不合适时调低范围并同步文案,不能只调测试等待。
  • 非法输入、动画、假后缀、损坏容器和超限文件有明确反馈,不产生可下载半成品。
  • 图片功能不依赖音频核心加载成功;生产模式的资源路径与缓存通过。
  • 现有工具入口、主题、页面布局和 iframe 使用没有回归。
  • 使用指南、格式清单、限制与核心许可资料齐全,实施记录标明已交付和剩余项。

10.4 仓库检查

仅提交本计划时执行文档登记与互指的既有测试,不增加镜像文案的测试,也不需要 为了 Markdown 正文跑整站 E2E:

npm run test -- tests/unit/docs-catalog.test.ts tests/unit/docs-xref.test.ts
git diff --check

后续功能实施时运行新单测、相关样式守卫、链接检查和构建,再跑使用新产物的相关 E2E。npm run e2e 本身不构建,修改源码后不能拿旧 .next 的测试结果验收。 如果类型检查出现已删除路由的幻影错误,按 ../CLAUDE.md 的构建说明处理; 必要时仅清理确认位于本仓的 .next/types 后重建,不顺手清空整个 .next。

11. 里程碑、交付与工作量

11.1 顺序与退出条件

里程碑 工作与交付 退出条件 估算开发人日
M0 技术验证 固定依赖候选、真实音频核心与原生图片探针、格式 / 设备 / 资源测量;有界比较轻量引擎 引擎选择、输入白名单与限制有测量依据;阻塞项写明 1–2
M1 页面与入口 文件处理分类、页面结构、词表、参数与任务状态、主题 / 响应式 可从真实菜单进入并完成选择 / 校验流程;尚不声称转换已经可用 0.5–1
M2 图片转换 原生解码 / 编码、透明背景、方向、动画拒绝、预览 / 下载、资源回收 图片矩阵与异常、布局、能力探测通过 1–2
M3 音频转换 同源资产准备、按需加载、探测 / 转码、串行、取消 / 超时、试听与输出验证 OGG 两种编码及其他首批输入通过真实引擎;失败后可恢复 1–2
M4 集成验收 真机 / iframe / 生产构建、资源测量、回归、使用指南和实施记录 §10 发布条件满足,已知限制可查 1–2

合计约 5–9 个开发人日,仅作安排工作的量级参考;不含等待真机、外部网络条件、 新增格式、服务器转换或修复超出预期的引擎兼容问题。M0 后根据结果重估。

里程碑是交付验收单位,不要求每期只有一个提交。按仓库约定,每个独立功能 / 修复 / 文档完成并验证后立即提交,不同语义分别提交;默认不 push。发布状态由实际验收 记录决定,不能把「有页面」「有输出」「已提交」当成全功能交付。

11.2 需要同步的文件

实施阶段 现有文件 / 目录 要同步的内容
本计划 docs/README.md、src/lib/docs-catalog.ts 文档索引与标题 / 摘要登记
M1 src/app/tool/ToolMenu.tsx、src/styles-scss/main.scss 入口、分类、搜索与样式导入
M2 / M3 package.json、package-lock.json、.gitignore 需要的音频依赖、资源准备 / 检查脚本、可重建资产
M4 playwright.config.ts、tests/e2e/tools.spec.ts 响应式用例登记、入口回归
M4 docs/architecture.md、docs/deploy.md 已实施的路由 / 模块、静态核心准备与故障排查
M4 docs/guide/、docs/README.md、src/lib/docs-catalog.ts 新使用指南、支持格式、限制、隐私与结果保真说明

架构和部署文档只在实际实现后更新为现状。现在不把未来路由、依赖或准备命令写成 已存在的能力;使用指南在功能实现时新增,不能提前让玩家进入不存在的转换页面。

12. 决策、实施记录与后续扩展

12.1 决策现状(2026-10-12 校对)

事项 当前状态 依据或剩余工作
统一格式转换器入口 已交付 /tool/convert 页面与工具箱入口已存在
图片+短音频优先 原始范围已扩为八个能力区 §12.2 记录实际交付;长期版图仍未全部实现
本地处理、匿名可用 已采用 现行页面无登录闸门,转换在浏览器内执行
FFmpeg 单线程基线 已集成 package.json 锁定 wrapper / core,scripts/copy-converter-assets.mjs 准备同源资产
Mediabunny 对比 / 替换 未验证;优化候选 有界比较仍缺记录,不作为已有功能,也不默认要求替换当前引擎
WebP 输出 已交付,按设备能力过滤 图片类别与 registry.ts 的 webp-encode 判据
文件 / 时间 / 内存限制 已有保护性限额;真机上限测量未完成 现行数值以 src/lib/file-converter/formats.ts 的 LIMITS 为准
依赖版本与核心路径 已锁定与登记 package-lock.json、引擎文件与资产复制脚本;不再是「尚未选择」
真实设备兼容性与性能矩阵 未完成 §10.2 / §10.3 要求的真机、浏览器、iframe 与资源测量不能由已有 Chrome 用例代替

12.2 实施记录

日期 交付 状态与证据
2026-10-08 计划文档、项目文档索引与站内目录登记 方案交付;M0–M4 均未实施,未新增运行时转换依赖
2026-10-08 长期能力规划与首版计划互指 八类文件的转换路线、保真边界与 R1–R5 建设目标已成文;仍属设计,未新增转换功能
2026-10-09 契约层(识别 / 登记表 / 队列 / 执行)+ 八个能力区的引擎与边 + 页面 + 用户指南 见下「2026-10-09 交付」

2026-10-09 交付(实测记录)

交付范围超出首版(图片 + 音频):八个能力区、共 81 条登记的边(79 条 live + 2 条 planned:office:html-print-pdf / office:docx-to-pdf,需要服务端组件,不进菜单), 全部在浏览器本地完成。 新增运行时依赖:@ffmpeg/ffmpeg + @ffmpeg/core(ST 核心)、pdfjs-dist、pdf-lib、 xlsx、mammoth、docx、pptxgenjs、tesseract.js + 中英语言包、heic2any、utif、 node-unrar-js、7z-wasm、iconv-lite、js-yaml、fast-xml-parser、fflate、marked。 引擎资产(约 66MB,13 个文件)不入库,由 scripts/copy-converter-assets.mjs 在 postinstall 与 build 生成。

验证命令与结果(2026-10-09,Windows / Chrome):

验证 命令 结果
单测 npm test 4186 passed / 187 files(其中格式转换器 16 个文件、377 例)
类型 npx tsc --noEmit 0 错误
构建 npm run build 成功;/tool/convert 首屏 185kB(重引擎全在动态 chunk 里)
端到端 npx playwright test tests/e2e/file-converter*.spec.ts 9 passed(含 PNG→JPG 与 WAV→FLAC→WAV 两条真转换用例,输出按字节魔数断言)
全量 e2e npm run e2e 524 passed / 4 failed / 4 skipped

偏离计划的地方(都已写进代码注释或 docs/architecture.md §6.17):

  • 首版只要求图片 + 短音频,实际按长期规划一次做到八类。
  • 压缩包只解 RAR / 7Z,不进 压:RAR 编码器是专有技术、7-Zip 的压缩侧没编进 wasm 构建。
  • pdf:pdf-to-searchable 的不可见文字层用 pdf-lib 标准字体(WinAnsi),覆盖不了中文 —— 该边默认语言恰好是 chi_sim+eng。目前中文只产出图像层,结果页 notice 如实说明; 要真正支持需引入可再分发的中文字体 + @pdf-lib/fontkit(未做)。
  • 首版计划里的 Mediabunny 对比、真实 iPhone / Android 设备矩阵未做。
  • roadmap §12.1 登记的十个字段里,「执行引擎与版本」「验收证据」没有单独建字段: 引擎与版本写在 engines/*.ts 的文件头(与 copy-converter-assets.mjs 的 版本常量对应),验收证据就是上表的测试与构建结果。真要为每条边存这两个字段, 七十多条边都要动一遍,收益不抵churn —— roadmap 说的是「建议逐步形成」, 记在这里当作有意推迟,别当成漏做。

已知限制(详见 docs/architecture.md §6.17 与各引擎文件头):音频 2 声道 / 5 分钟上限; 视频 1080p / 2 分钟上限;取消或超时需重启整个 FFmpeg 核心(0.12 无软中断)。

当时全量 e2e 的 4 个失败在 tests/e2e/editor-mobile-toolbar.spec.ts,与格式转换器无关, 本次之前就红 —— 已用隔离 worktree 在基准 commit 上复现(数字逐位一致),二分定位到 68f0a35e「fix(blog):窄屏下发文 / 编辑卡片的内边距收到 16px」:窄屏内边距从 40px 收到 16px 后编辑区整体左移,[data-more='open'] 展开时「导出」分组落到靠左位置,而导出浮层是 右锚向左展开(.md-toolbar__menu--right { left:auto; right:0 }),于是在 320px 下越过编辑器 左缘(23.6 < 31.5)、390px 下越过视口左缘(-60.4)。归编辑器的布局缺口,未在本轮修。 2026-10-12 校对:该布局缺口已由 **3c8fbf8d(2026-10-10)**修复,窄屏导出组增加 margin-left: auto 保持右锚菜单在编辑器内;上表失败数保留为 2026-10-09 的历史结果, 本次文档整理没有重跑全量 e2e,不能把它写成当前仍失败或当前全绿。

后续每次交付补充:提交、已完成的范围、设备 / 版本、验证命令与结果、限制、偏离 计划的原因和剩余工作。测量记录使用自制样本,不在公开文档中留下用户文件或 私密录音;本目录文档会被 /docs 对外展示。

12.3 原扩展建议的交付状态(2026-10-12 校对)

原建议 已交付范围 剩余边界
更多音频输出 FLAC、OGG Vorbis / Opus、AAC M4A、AIFF 与换封装 设备样本与播放兼容性仍需验收,不能宣称所有组合无条件可用
图片缩放与更多格式 缩放参数、AVIF / BMP / TIFF / ICO 输出、HEIC / TIFF 输入与动画相关边 按浏览器能力过滤;不意味着 HEIC 输出、RAW 与专业色彩能力已交付
ZIP 打包下载 成功结果打包及压缩包类别 仍受结果预算与压缩包安全上限约束
视频提取音频 / 短视频转换 视频类别、提音频 / 字幕、抽帧与动图 当前限 1080p / 2 分钟;真机资源测量未完成
服务端转换 未实施 上传、调度、隔离、临时文件与隐私契约仍需单独设计
Office / PDF DOCX 内容转换、图片型 PDF→DOCX / PPTX、PDF 操作与 OCR DOCX / HTML 排版导出 PDF 仍为 planned;中文可搜索 PDF 的文字层尚缺字体方案

尚未交付的方向、验收缺口与有意推迟项统一见 docs/plan/format-converter-roadmap.md §16。 每次扩展仍先定义输入 / 输出矩阵和验收样本;本节只核对原建议,避免把已有功能重复列为待办。

本页内容来自仓库 docs/format-converter-implementation.md·在 GitHub 上查看

格式转换器首轮实施记录 · 聪明山