前端大文件上传的时候,采用切片上传的方式,如果断网了,应该如何处理

我们前端在大文件上传的时候基本上都会考虑切片上传的方式,那么在切片上传的场景中,断网是最常见的异常情况之一,如果想要断网恢复之后,继续上传,就需要通过断点续传机制来处理。刚好公司做了云盘的功能,遇到了这个切片上传的问题,总结一下,分享给大家~

这个问题的核心目标是:断网后不丢失已上传进度,网络恢复后能从断点继续上传,避免重复上传已完成的切片

一、断网检测与状态捕获

首先需要准确检测网络状态变化,以便及时触发中断处理逻辑:

  1. 网络状态监听
    利用浏览器提供的 navigator.onLine 属性和 online/offline 事件监听网络状态:
javascript 复制代码
// 监听网络断开
window.addEventListener('offline', () => {
  console.log('网络已断开,暂停上传');
  pauseUpload(); // 触发上传暂停逻辑
});

// 监听网络恢复
window.addEventListener('online', () => {
  console.log('网络已恢复,尝试继续上传');
  resumeUpload(); // 触发续传逻辑
});

React 结合Hooks来实现

使用 useEffect 进行事件绑定 / 解绑,确保与组件生命周期同步

初始状态通过 navigator.onLine 获取,避免初始值与实际状态不一致

javascript 复制代码
import { useState, useEffect } from 'react';

function NetworkStatus() {
  // 状态管理:是否在线
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    // 定义事件处理函数
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    // 绑定事件监听
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    // 组件卸载时清除监听(避免内存泄漏)
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []); // 空依赖数组:只在组件挂载时执行一次

  return (
    <div>
      <p>网络状态:{isOnline ? '在线' : '离线'}</p>
    </div>
  );
}
  1. 请求层面的异常捕获
    即使网络未完全断开(如弱网超时),单个切片上传请求也可能失败,需在 fetchaxios 中捕获异常:
javascript 复制代码
async function uploadChunk(chunk) {
  try {
    const response = await fetch('/upload-chunk', {
      method: 'POST',
      body: chunk.formData,
    });
    if (!response.ok) throw new Error('请求失败');
    return { success: true, chunkIndex: chunk.index };
  } catch (error) {
    console.log(`切片 ${chunk.index} 上传失败`, error);
    return { success: false, chunkIndex: chunk.index };
  }
}

二、断网时的核心处理逻辑

断网后需保存当前上传状态,为后续续传提供依据:

  1. 保存上传进度状态
    将已上传的切片信息、文件元数据等保存到本地存储(如 localStorageIndexedDB,后者更适合大量数据):
javascript 复制代码
// 定义需要保存的上传状态结构
const uploadState = {
  fileId: 'xxx', // 文件唯一标识(如哈希值)
  fileName: 'large-file.zip',
  totalChunks: 50, // 总切片数
  uploadedChunks: [0, 1, 2, ...], // 已成功上传的切片索引
  chunkSize: 2 * 1024 * 1024, // 切片大小
  lastUploadedChunk: 23, // 最后一次成功上传的切片索引
  fileSize: 100 * 1024 * 1024, // 文件总大小
};

// 保存到 IndexedDB(适合大文件状态)
function saveUploadState(state) {
  return indexedDB.transaction('rw', db.uploadStates, () => {
    db.uploadStates.put(state);
  });
}
  1. 暂停上传队列
    如果使用并发上传(如同时上传3个切片),断网后需终止当前正在进行的请求,并清空等待队列:
ini 复制代码
let uploadQueue = []; // 等待上传的切片队列
let activeRequests = []; // 当前正在进行的请求

function pauseUpload() {
  // 终止所有活跃请求
  activeRequests.forEach(abortController => abortController.abort());
  // 清空队列(后续续传时重新生成)
  uploadQueue = [];
}
  1. 用户体验提示
    向用户展示明确的状态提示(如"网络已断开,已保存上传进度"),避免用户误以为上传失败或需要重新开始。

三、网络恢复后的续传逻辑

网络恢复后,核心是基于已保存的状态,仅上传未完成的切片

  1. 恢复上传状态
    从本地存储中读取之前保存的 uploadState,确认文件信息和已上传切片:
javascript 复制代码
async function getSavedState(fileId) {
  return indexedDB.transaction('r', db.uploadStates, () => {
    return db.uploadStates.get(fileId);
  });
}
  1. 与服务端校验已上传切片
    为避免本地状态与服务端不一致(如断网时某切片已上传成功但前端未记录),需向服务端查询当前文件已接收的切片:
javascript 复制代码
async function getServerUploadedChunks(fileId) {
  const response = await fetch(`/get-uploaded-chunks?fileId=${fileId}`);
  return response.json(); // 服务端返回已接收的切片索引数组
}
  1. 生成待上传切片队列
    对比本地已上传切片和服务端返回的切片,计算出未上传的切片,生成新的上传队列:
javascript 复制代码
async function resumeUpload(fileId) {
  // 1. 读取本地保存的状态
  const localState = await getSavedState(fileId);
  if (!localState) {
    console.log('无保存的上传状态,需重新上传');
    return;
  }

  // 2. 向服务端校验已上传切片
  const serverChunks = await getServerUploadedChunks(fileId);

  // 3. 计算未上传的切片(取本地和服务端的并集,排除已上传的)
  const allChunks = Array.from({ length: localState.totalChunks }, (_, i) => i);
  const uploadedChunks = [...new Set([...localState.uploadedChunks, ...serverChunks])];
  const pendingChunks = allChunks.filter(index => !uploadedChunks.includes(index));

  // 4. 重新发起上传(仅上传未完成的切片)
  uploadPendingChunks(localState, pendingChunks);
}
  1. 继续上传未完成切片
    按原有的并发策略(如同时上传3个),逐个上传 pendingChunks 中的切片,并实时更新本地状态和服务端记录。

四、额外的可靠性保障

  1. 文件唯一标识
    用文件内容的哈希值(如 md5sha256)作为 fileId,确保即使文件名相同但内容不同时,也能正确区分上传进度。
  2. 切片上传的幂等性
    服务端需支持重复上传同一切片(即多次上传同一 fileId + chunkIndex 时,不会重复存储,直接返回成功),避免网络抖动导致的重复上传问题。
  3. 定期保存状态
    不仅在断网时保存状态,每成功上传一个切片后都更新本地存储,降低因页面刷新/关闭导致的进度丢失风险。
  4. 超时重试机制
    网络恢复后,若首次续传失败(如服务端暂时不可用),可设置有限次数的重试(如最多重试3次),进一步提升可靠性。

总结

断网处理的核心是 "状态保存-校验-续传" 闭环:

  1. 断网时通过事件监听捕获异常,保存已上传进度到本地;
  2. 网络恢复后,结合本地状态和服务端校验,确定未上传的切片;
  3. 仅上传未完成的切片,最终完成整个文件的上传和合并。

总结一下,以免遗忘~

相关推荐
培根芝士9 分钟前
Electron将视频文件单独打包成asar并调用
前端·javascript·electron
德育处主任9 分钟前
p5.js 3D模型(model)入门指南
前端·前端框架·canvas
小小小小宇17 分钟前
React hook的执行顺序
前端
curdcv_po31 分钟前
🔥🔥🔥结合 vue 或 react,去写three.js
前端·react.js·three.js
猫头_1 小时前
uni-app 转微信小程序 · 避坑与实战全记录
前端·微信小程序·uni-app
天生我材必有用_吴用1 小时前
网页接入弹窗客服功能的完整实现(Vue3 + WebSocket预备方案)
前端
海拥1 小时前
8 Ball Pool:在浏览器里打一局酣畅淋漓的桌球!
前端
Cache技术分享1 小时前
148. Java Lambda 表达式 - 捕获局部变量
前端·后端
明长歌1 小时前
【javascript】Reflect学习笔记
javascript·笔记·学习
YGY Webgis糕手之路1 小时前
Cesium 快速入门(二)底图更换
前端·经验分享·笔记·vue