题干与适用场景
假设 CSV 为 2 GB,主线程内存预算 20 MB,并要求每个分片确认后 100 毫秒内更新进度。用户可能刷新页面或离线 30 分钟。客户端必须无需重新读取整文件即可恢复,保持界面响应,并且只有服务端确认后才能宣称分片已上传。OPFS 属于页面来源私有存储,与用户可见的文件夹句柄不同。
面试官考察点
- 根据文件大小、持久性、权限和浏览器支持选择存储原语。
- 把解析和字节 I/O 移出主线程,同时保持 UI 状态一致。
- 定义包含分片身份、校验和、重试及服务端对账的恢复协议。
- 处理配额压力、驱逐、取消、隐私和不支持浏览器。
回答前需要澄清的问题
确认刷新后是否必须保留原文件、服务端是否接受乱序分片、行是否能增量解析,以及目标浏览器范围。若服务端能直接读取用户选择的文件,本地持久化可能不必要;若必须在刷新后恢复,OPFS 或其他持久存储就成为方案的一部分。
30 秒回答框架
我会在 OPFS 保存包含 upload_id、源文件指纹、分片大小、已确认范围和校验状态的小型清单。专用 Worker 读取有界切片,先持久化分片再标记待上传,并使用幂等分片键上传。服务端返回已接受范围,重试先对账再决定。主线程只接收节流后的进度消息,并负责取消与无障碍状态。通过能力检测、配额处理和用户重新选择文件的降级流程覆盖不支持或被驱逐的客户端。
分步骤深入解答
1. 选择存储和边界
把清单和少量元数据放在持久浏览器存储,把大块临时分片放入 OPFS。OPFS 属于来源私有存储,访问通常不需要权限提示;用户可见的文件句柄使用另一套权限模型,需要明确手势。同步访问句柄或其他阻塞磁盘工作放在 Worker 中,主线程不等待 I/O。
2. 让恢复成为协议,而不是进度条
生成 uploadid 和确定性的分片编号。每次请求携带 uploadid、分片索引、字节范围、长度和校验和。服务端记录已接受范围,并在状态接口返回这些范围。相同分片身份的重试必须幂等,校验不匹配则拒绝。刷新后 Worker 读取清单,向服务端询问已接受范围,只上传缺失集合。
3. 保持 UI 响应并可恢复
读取固定大小切片,每次只传递一个有界缓冲区,并节流进度事件,避免渲染和解析争抢。网络尝试前持久化清单,服务端确认后再次持久化。取消时本地标记、终止活动请求并安排清理;在服务端确认终止前不能删除唯一的恢复元数据。
4. 处理配额、兼容性和安全
复制文件前检查可用存储,并展示可恢复的“存储已满”状态。OPFS 不可用或被驱逐时,降级到用户重新选择文件的流程,从服务端已知范围继续;该模式不能承诺离线恢复。文件名和解析行都视为不可信,上传需要认证授权,不能暴露本地路径。测试刷新、离线、重复分片、校验失败、标签崩溃、配额拒绝和 Worker 重启。
高质量示范回答
我会先确认浏览器范围、能否流式解析,以及是否必须支持刷新恢复。客户端在 OPFS 保存清单和有界临时分片,由 Worker 按确定性分片身份读取并上传。服务端掌握已接受范围,重试先对账并校验。主线程只渲染节流进度并负责取消。存储不可用时切换到重新选择文件的流程,并明确失去离线持久化,但仍可按服务端范围恢复。配额、驱逐、来源私有语义和清理都应成为可观测状态。
常见错误
- 把整文件读入内存 → 2 GB 输入会卡死或崩溃标签页 → 在 Worker 中读取有界分片。
- 把进度百分比当作事实 → 丢失确认会造成空洞或重复 → 以服务端已接受范围对账。
- 认为 OPFS 是用户可见文件夹 → 用户无法按同样方式查看或授权 → 说明来源私有存储,需要导出时使用文件句柄。
- 只在上传成功后持久化 → 崩溃会丢失下一次重试位置 → 尝试前和确认后都保存清单。
- 忽略配额和驱逐 → 离线恢复会无提示失败 → 检查容量并提供明确降级。
- 信任文件名或 CSV 字段 → 本地数据可能注入内容或泄露路径 → 校验输入且不暴露本地路径。
追问及应对
服务端已收到分片,但清单更新前标签页崩溃,怎么办?
重启时向服务端查询已接受范围,以服务端结果为准。清单缺少的分片可以用相同分片身份再次上传,服务端应返回已有结果而不是保存重复分片。
浏览器报告 OPFS 被驱逐,要让导入失败吗?
保留上传记录,说明离线恢复已不可用。让用户重新选择源文件;源指纹匹配时继续使用服务端范围,否则开始新的上传。
为什么不对每次导入都使用用户可见目录句柄?
它增加权限和手势流程,也会受到用户外部修改目录的影响。需要编辑或导出原文件时使用目录句柄;临时私有分片使用 OPFS。
如何避免 Worker 把网络打满?
限制并发分片数量,服务端或浏览器出现背压时暂停读取,并优先处理重试。暴露队列深度和重试年龄,让 UI 能解释进度变慢。