4.7 GB 视频导致浏览器内存溢出(OOM)的解决方案

发布于:2025-08-15 ⋅ 阅读:(17) ⋅ 点赞:(0)

在线教育平台大文件视频上传解决方案

场景

我们要做一个讲师后台的课程视频批量上传功能
需求听起来很简单:一次拖 20 个 4K 视频,浏览器稳、速度快、断网可续传。

第一版我图省事,用了:

<input type="file" />

配合:

const formData = new FormData();
formData.append('file', file);
fetch('/upload', { method: 'POST', body: formData });

结果上线当天就爆了:

  • 讲师 A:上传一个 4.7 GB 的 .movChrome 直接内存溢出崩溃

  • 讲师 B:断网 3 分钟 → 重新上传进度条从 0% 开始,心态崩

  • 运营同事:疯狂 @ 前端,“是不是没做分片?”

于是,第二版我设计了三层防线,核心思路就是——把大文件切成小片,让浏览器分开调用。


第一层防线:分片 + 并发控制

思路:4 GB 文件切成一堆小块(比如每块 2 MB),同时上传 5 块,既不卡浏览器也不占满带宽。

// slice.js
const CHUNK_SIZE = 2 * 1024 * 1024; // 2 MB 一块

export async function* sliceFile(file) {
  let cur = 0;
  while (cur < file.size) {
    yield file.slice(cur, cur + CHUNK_SIZE);
    cur += CHUNK_SIZE;
  }
}
// uploader.js
import pLimit from 'p-limit';
import { sliceFile } from './slice.js';

export async function upload(file) {
  const limit = pLimit(5);              // 同时最多 5 个请求
  const hash = await calcHash(file);    // 用于秒传 + 断点续传
  const tasks = [];

  for await (const chunk of sliceFile(file)) {
    tasks.push(limit(() => uploadChunk({ hash, chunk })));
  }

  await Promise.all(tasks);
  await mergeChunks(hash, file.name);   // 通知后端合并
}

📌 好处:

  1. file.slice 原生 API,不占额外内存

  2. p-limit 控制并发,避免一次性发出几百个请求打爆浏览器

  3. calcHash 用 WebWorker 算 MD5,不会卡 UI


第二层防线:断点续传

断点续传要解决的关键问题:续在哪?

我们需要一个“进度表”,记录哪些分片已经上传。

存储位置 内容 生命周期
前端 IndexedDB hash → 已上传分片索引数组 本地有效,清缓存失效
后端 Redis/MySQL hash → 已接收分片索引数组 可设置 TTL,支持跨设备续传

交互流程:

sequenceDiagram
  participant F as 前端
  participant B as 后端

  F->>B: POST /prepare {hash, totalChunks}
  B-->>F: 200 OK {uploaded: [0,3,7]}

  loop 上传剩余分片
    F->>B: POST /upload {hash, index, chunkData}
    B-->>F: 200 OK
  end

  F->>B: POST /merge {hash}
  B-->>F: 200 OK

第三层防线:协议可插拔

上传逻辑抽象成一个统一接口:

interface Uploader {
  prepare(file: File): Promise<PrepareResp>;
  upload(chunk: Blob, index: number): Promise<void>;
  merge(): Promise<string>;  // 返回文件 URL
}

这样我们可以快速切换:

  • BrowserUploader → 纯前端分片上传

  • TusUploadertus.io 协议,天然断点续传

  • AliOssUploader → 直传阿里 OSS,利用 SDK 自带的断点续传

方案 并发控制 断点续传 秒传 代码量
自研 手动实现 自己实现 手动 300 行
tus 内置 协议级 需后端 100 行
OSS 内置 SDK 级 自动 50 行

加强功能

1. 秒传(Instant Upload)

先算 hash → 调 /exists?hash=xxx → 如果已存在,直接返回 URL,不走上传流程。

2. 加密上传

uploadChunk 前用 AES-GCM 加密数据块,后端存加密内容,下载时前端解密。

3. P2P 协同上传

用 WebRTC 让同一局域网的浏览器互传分片,然后统一上报,节省出口带宽。


小结

大文件上传的核心不是“传”,而是“断”。

  • 把大文件切成 2 MB 小块

  • 记住哪些块已经上传

  • 支持灵活切换上传协议

这样,浏览器就能稳稳地“吃下”任何体积的视频,哪怕是 4 GB 以上的大文件。


网站公告

今日签到

点亮在社区的每一天
去签到