增加语音功能
1. 数据库扩展(D1)
在 sentences 表中增加音频相关字段:
ALTER TABLE sentences ADD COLUMN audio\_path TEXT; \-- R2对象键,如 "sentences/user123/abc.mp3"
ALTER TABLE sentences ADD COLUMN duration INTEGER; \-- 音频时长(秒),用于UI显示
ALTER TABLE sentences ADD COLUMN waveform\_data TEXT; \-- 可选,预生成波形JSON(若使用wavesurfer可跳过)
若您未来支持用户录制上传,还需一张 audio_uploads 日志表(非必须),便于追踪。
2. R2 存储配置
- 创建存储桶:在 Cloudflare Dashboard 创建 R2 存储桶,例如
sentence-audio。 - 访问权限:
- 建议不公开读取,而是通过 Worker 生成临时签名 URL,这样可防盗链、控制过期时间,同时便于统计流量。
- 若追求极致简单,也可设为公开读取,但需承担被盗链的风险(流量可能超出免费额度)。
- 建议不公开读取,而是通过 Worker 生成临时签名 URL,这样可防盗链、控制过期时间,同时便于统计流量。
- 存储类别:务必选择 Standard,因为免费出口流量仅适用于标准存储(每月 1GB 出站流量)。
- 文件命名:推荐按用户隔离,如
audio/{userId}/{sentenceId}_{timestamp}.mp3,避免冲突。
3. 后端 API 扩展(Worker)
3.1 上传音频接口
POST /api/sentences/:id/audio
Content-Type: multipart/form-data
- 身份验证:从请求头获取 session,验证用户是否拥有该句子。
- 接收文件(限制大小,如 5MB),使用
audio/mpeg验证。 - 生成随机文件名,使用
await env.R2_BUCKET.put(key, file)上传。 - 获取文件元数据(如
httpMetadata.contentType),并获取时长(可使用fluent-ffmpeg或mediainfo,但 Worker 环境不便,建议前端上传时同时传递时长)。 - 更新
sentences表中的audio_path和duration。 - 返回
{ audioUrl: signedUrl }。
3.2 获取音频签名 URL(或直接播放)
若使用签名 URL,提供:
GET /api/sentences/:id/audio?expires=...
Worker 验证身份,生成 R2 签名 URL(有效期 1 小时),返回给前端。
更推荐的方式:直接在 Worker 中反向代理音频流,由 Worker 校验身份后,从 R2 读取并返回。这样完全隐藏 R2 地址,且可灵活设置缓存策略。
此方式不会产生额外费用(Worker 请求数在免费额度内),且方便添加 Referer 校验等防盗链。
4. 前端音频播放与交互
4.1 播放控制
在句子卡片或详情页,添加播放按钮
4.2 A-B 复读功能
- 用户可通过设置起点
A和终点B(例如通过拖动进度条或点击按钮标记当前时间)。 - 利用
ontimeupdate监听currentTime,当currentTime >= B时跳回A。
可提供“设置A点”、“设置B点”按钮,以及开启/关闭复读的开关。
4.3 波形可视化(集成 wavesurfer.js)
- 安装
wavesurfer.js:npm install wavesurfer.js - 在组件中创建 Wavesurfer 实例,挂载到容器。
注意:R2 默认支持
Range请求,wavesurfer 通过MediaElement后端可直接使用音频 URL,无需额外配置,且能边下边播。
5. 成本与免费额度优化
| 组件 | 免费额度 | 本方案消耗 |
|---|---|---|
| R2 存储 | 10 GB 存储,1 GB/月下载流量(Standard) | 音频文件大小需控制(建议≤2MB/条),总存储量在10GB内 |
| R2 操作 | 每月 1000 万次 A/B 类操作,100 万次 C 类操作 | 上传/下载操作极少,远低于限制 |
| Worker 请求 | 10 万次/天 | 播放请求计入,正常使用可覆盖 |
优化策略:
- 音频压缩:使用 MP3 128kbps 或更低,单条时长<30秒,文件体积<1MB。
- 缓存:在 Worker 响应中增加
Cache-Control: public, max-age=3600,减少重复从 R2 读取。 - 上传前压缩:前端录制时直接使用较低比特率,避免上传大文件。
- 只存储用户上传/录制的音频,若使用 TTS,可考虑每次实时生成但不存储(或缓存一段时间),权衡存储成本。
6. 防盗链与安全
- Worker 代理模式:所有音频请求必须经过 Worker,Worker 校验用户身份(会话),确保用户只能访问自己的音频。
- 限制 Referer:可选检查
Referer是否来自您的域名,但容易被伪造,更可靠的是使用签名 URL。 - 生成短时效签名 URL(如 5 分钟有效),前端通过 API 获取后立即使用。
7. 前端交互流程(用户视角)
- 用户进入句子详情页,点击“录制/上传音频”按钮。
- 使用浏览器
getUserMedia或选择文件,上传至后端。 - 后端存入 R2,更新数据库,返回音频路径。
- 页面显示波形(Wavesurfer)和播放控制条。
- 用户可拖动波形设置 A/B 点,开启复读。
- 点击播放,音频从 R2 流式加载并播放。
8. 扩展性与未来
- 若需支持多音频版本(如用户多次录制),可设计
audio_versions表,并与句子关联。 - 若需TTS 自动生成,可调用 Cloudflare AI(如 Workers AI)的文本转语音模型,但需注意费用,建议限定每日次数。
9. 小结
本方案完全基于 Cloudflare 免费资源,在现有 D1 + Worker 架构上平滑扩展,利用 R2 标准存储的免费出口流量,通过 Worker 代理实现安全、可缓存的音频播放,并集成了现代化的波形交互与 A-B 复读功能。整体设计遵循“简单、成本低、可扩展”的原则,与项目初衷高度一致。
如需具体代码片段或 Worker 路由配置细节,可进一步沟通。祝您的英语复习应用越做越好! 🚀