RNFetchBlob多文件并行下载如何实现断网暂停自动续传
断网自动暂停+断点续传多文件下载实现方案
核心实现逻辑
整体拆为4个模块:网络状态实时监听、单任务断点续传能力、队列任务保活机制、断网任务终止/恢复调度。
完整实现代码
1. 基础依赖与全局状态定义
需要先安装网络监听库:yarn add @react-native-community/netinfo
import RNFetchBlob from 'rn-fetch-blob' import NetInfo from "@react-native-community/netinfo"; // 全局状态 let isNetworkAvailable = true; let files_que = [...items]; // 存储每个文件的断点位置:key为文件唯一标识,value为已下载字节数 const breakPointMap = new Map(); // 存储正在执行的下载任务句柄,断网时可批量取消 const activeDownloadTasks = new Map(); // 并发下载数 const CONCURRENT_LIMIT = 2;
2. 支持断点续传的单文件下载方法
/** * 断点续传下载单文件 * @param {string} url 下载地址 * @param {string} savePath 本地存储路径 * @param {string} fileKey 文件唯一标识 */ const processdownloadVideo = (url, savePath, fileKey) => { return new Promise(async (resolve, reject) => { // 读取本地已下载文件大小,作为续传起始点 let downloadedSize = 0; try { const fileStat = await RNFetchBlob.fs.stat(savePath); downloadedSize = fileStat.size; breakPointMap.set(fileKey, downloadedSize); } catch (err) { // 文件不存在则从0开始下载 downloadedSize = 0; } // 当前无网络直接挂起 if (!isNetworkAvailable) { return reject({ type: 'network_suspend', fileKey }); } // 发起带Range头的续传请求 const downloadTask = RNFetchBlob.config({ path: savePath, appendExt: false, // 开启追加写入模式 }) .fetch('GET', url, { Range: `bytes=${downloadedSize}-`, }) .progress({ interval: 250 }, (received) => { // 实时更新断点位置 breakPointMap.set(fileKey, downloadedSize + received); }); activeDownloadTasks.set(fileKey, downloadTask); downloadTask.then(async (res) => { breakPointMap.delete(fileKey); activeDownloadTasks.delete(fileKey); // 200为全量下载成功、206为断点续传成功 const status = res.info().status; if (status === 200 || status === 206) { resolve(res); } else { reject({ type: 'download_error', err: new Error(`HTTP状态异常:${status}`), fileKey }); } }).catch((err) => { activeDownloadTasks.delete(fileKey); reject( isNetworkAvailable ? { type: 'download_error', err, fileKey } : { type: 'network_suspend', fileKey } ); }); }); };
3. 支持断网等待的下载队列worker
const downloadWorker = async () => { while (files_que.length > 0) { // 无网络时循环等待,不消费队列 if (!isNetworkAvailable) { await new Promise(resolve => { const waitTimer = setInterval(() => { if (isNetworkAvailable) { clearInterval(waitTimer); resolve(); } }, 1000); }); } const currentFile = files_que.shift(); const fileKey = currentFile.uri; const savePath = `path/${currentFile.uri}`; try { await processdownloadVideo(currentFile.http_uri, savePath, fileKey); } catch (err) { if (err.type === 'network_suspend') { // 断网导致的中断,把任务放回队列头部,恢复后优先下载 files_que.unshift(currentFile); await new Promise(resolve => setTimeout(resolve, 1000)); } else { // 非网络错误可自定义重试逻辑,比如最多重试3次再标记失败 console.log('文件下载失败', err); } } } };
4. 网络监听与下载启动逻辑
// 初始化网络状态监听 const initNetworkMonitor = () => { return NetInfo.addEventListener(state => { const currentNetStatus = state.isConnected && state.isInternetReachable; // 联网切断网:取消所有正在进行的下载任务,避免资源占用 if (isNetworkAvailable && !currentNetStatus) { isNetworkAvailable = false; activeDownloadTasks.forEach(task => task.cancel()); activeDownloadTasks.clear(); } // 断网切联网:更新状态,worker会自动恢复消费队列 if (!isNetworkAvailable && currentNetStatus) { isNetworkAvailable = true; } }); }; // 启动多文件下载 const startBatchDownload = () => { const removeNetListener = initNetworkMonitor(); const workers = Array(CONCURRENT_LIMIT).fill(null).map(() => downloadWorker()); return Promise.all(workers).finally(() => removeNetListener()); };
注意事项
- 服务端必须支持
Range请求头,否则断点续传无法生效,大部分静态资源服务默认开启该能力,自研服务需要单独配置 - 如果需要APP重启后续传,可以将
files_que和breakPointMap持久化到本地存储(比如AsyncStorage),APP启动时读取数据恢复队列即可 - 建议给非网络类错误增加重试次数限制,避免异常任务一直阻塞队列
- 并发数建议设置为2-3,过高容易触发服务端限流,也会占用过多设备带宽
内容的提问来源于stack exchange,提问作者Rover
相关产品推荐
相关产品推荐

