前端大文件上传

为什么要分片上传?

  1. 大文件直接上传容易超时、中断、服务器压力大
  2. 分片失败可以重传单个分片,不用重传整个文件
  3. 支持并发上传,速度更快

前端负责

  1. 哈希(给文件生成唯一 ID;MD5:是哈希里最常用、最简单的一种算法)
  2. 分片(把大文件切成小碎片)
  3. 分片上传 / 断点续传(查询是否上传,服务器如有,可直接过滤)
  4. 通知后端合并

后端负责

1. 校验接口(秒传 / 断点续传用)

前端传:文件hash

文件不存在 → 返回已上传的分片序号
2. 上传分片接口

前端传:分片文件 + 文件hash + 分片序号

保存这个小分片到服务器
3. 合并接口

前端传:文件hash + 文件名

把所有分片按顺序合并成完整文件

完整代码

html 复制代码
<input type="file" id="file">
<button onclick="upload()">上传</button>

<script src="https://cdn.bootcdn.net/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script>
<script>
const CHUNK_SIZE = 2 * 1024 * 1024;

// 计算hash
function getHash(file) {
  return new Promise((resolve, reject) => {
    const spark = new SparkMD5.ArrayBuffer();
    const reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = e => { spark.append(e.target.result); resolve(spark.end()) };
    reader.onerror = reject;
  });
}

// 上传单个分片(带失败捕获)
async function uploadChunk(chunk, hash, index) {
  const fd = new FormData();
  fd.append("chunk", chunk);
  fd.append("hash", hash);
  fd.append("index", index);

  const res = await fetch("http://localhost:3000/upload-chunk", {
    method: "POST",
    body: fd
  });

  if (!res.ok) throw new Error(`分片 ${index} 上传失败`);
}

// 主上传(完整版)
async function upload() {
  try {
    const file = document.getElementById("file").files[0];
    if (!file) return alert("请选择文件");

    const hash = await getHash(file);

    // 切片
    const chunks = [];
    for (let i = 0; i < file.size; i += CHUNK_SIZE) {
      chunks.push(file.slice(i, i + CHUNK_SIZE));
    }

    // 1. 校验已上传分片(断点续传核心)
    const verifyRes = await fetch("http://localhost:3000/verify", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ hash })
    });
    const { uploaded } = await verifyRes.json();

    // 2. 只传缺失的分片(失败自动抛错)
    for (let i = 0; i < chunks.length; i++) {
      if (uploaded.includes(i)) {
        console.log("跳过分片:", i);
        continue;
      }
      await uploadChunk(chunks[i], hash, i);
    }

    // 3. 全部传完 → 合并
    const mergeRes = await fetch("http://localhost:3000/merge", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ hash, filename: file.name })
    });

    if (!mergeRes.ok) throw new Error("合并失败");
    alert("上传成功!");
  } catch (err) {
    // ✅ 所有错误统一捕获!
    console.error(err);
    alert("上传失败:" + err.message);
  }
}
</script>
相关推荐
这儿有一堆花15 分钟前
深入解析 Video.js:现代 Web 视频播放的工程实践
前端·javascript·音视频
烤麻辣烫31 分钟前
JS基础
开发语言·前端·javascript·学习
IT_陈寒1 小时前
Vue的响应式把我坑惨了,原来问题出在这
前端·人工智能·后端
2603_953527991 小时前
WordPress Finale Lite 插件高危漏洞检测与利用工具 (CVE-2024-30485)
前端·python·安全·web3·xss
2601_949818091 小时前
头歌答案--爬虫实战
java·前端·爬虫
猫猫不是喵喵.2 小时前
layui表单项次大数据量导入并提交
前端·javascript·layui
张小潇2 小时前
AOSP15 WMS/AMS系统开发 - 窗口层级源码分析
android·前端
whuhewei3 小时前
HTTP1/2/3演变
前端·计算机网络
腹黑天蝎座3 小时前
从零实现一个前端监控系统:性能、错误与用户行为全方位监控
前端·监控
Hooray4 小时前
为了在 Vue 项目里用上想要的 React 组件,我写了这个 skill
前端·ai编程