在React Native应用中实现批量PDF文件下载,需要兼顾权限适配、任务调度、存储管理、进度反馈等多个环节,才能保障下载过程的稳定与用户体验的流畅。下面将从完整实现流程出发,讲解具体的实践方案。

前期准备与权限申请
批量下载PDF文件前,需要先完成相关依赖的安装和权限申请,避免因权限缺失导致下载失败。
首先安装文件下载和权限处理相关的依赖:
npm install react-native-fs react-native-permissions react-native-background-downloader
对于Android平台,需要在AndroidManifest.xml中添加存储权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" />
对于iOS平台,需要在Info.plist中添加网络请求权限和文件访问权限配置。
权限申请的核心代码如下:
import {request, PERMISSIONS} from 'react-native-permissions';
import {Platform} from 'react-native';
// 申请存储权限
const requestStoragePermission = async () => {
if (Platform.OS === 'android') {
const result = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
return result === 'granted';
}
// iOS无需额外申请存储权限
return true;
};
批量下载核心逻辑实现
批量下载需要维护下载任务队列,避免同时发起过多请求导致资源占用过高,同时需要同步每个任务的下载进度和状态。
定义下载任务管理类
我们可以封装一个下载管理器,统一处理批量下载的调度、进度监听和结果回调:
import BackgroundDownloader from 'react-native-background-downloader';
import RNFS from 'react-native-fs';
import {Platform} from 'react-native';
class PdfBatchDownloader {
constructor() {
// 下载任务队列
this.taskQueue = [];
// 正在下载的任务数量
this.downloadingCount = 0;
// 最大并发下载数
this.maxConcurrent = 3;
// 下载任务状态映射 key为文件id 值为状态和进度
this.taskStatusMap = {};
// 下载完成回调
this.onAllComplete = null;
// 单个任务进度回调
this.onTaskProgress = null;
}
// 设置批量下载的PDF文件列表 每个元素包含id url saveName字段
setDownloadList(list) {
this.taskQueue = list.map(item => ({
...item,
status: 'pending', // pending downloading done failed
progress: 0,
}));
list.forEach(item => {
this.taskStatusMap[item.id] = {status: 'pending', progress: 0};
});
}
// 开始批量下载
async startBatchDownload() {
const hasPermission = await requestStoragePermission();
if (!hasPermission) {
console.log('存储权限未授予,无法下载');
return;
}
this.checkAndStartNextTask();
}
// 检查并启动下一个待下载任务
checkAndStartNextTask() {
// 如果正在下载的数量达到上限 或者没有待下载任务 则停止
if (this.downloadingCount >= this.maxConcurrent || this.taskQueue.length === 0) {
// 检查所有任务是否都完成
const allDone = Object.values(this.taskStatusMap).every(item => item.status === 'done' || item.status === 'failed');
if (allDone && this.onAllComplete) {
this.onAllComplete();
}
return;
}
// 找到第一个pending状态的任务
const pendingTaskIndex = this.taskQueue.findIndex(item => item.status === 'pending');
if (pendingTaskIndex === -1) return;
const task = this.taskQueue[pendingTaskIndex];
task.status = 'downloading';
this.taskStatusMap[task.id].status = 'downloading';
this.downloadingCount++;
this.downloadSinglePdf(task);
}
// 下载单个PDF文件
downloadSinglePdf(task) {
const savePath = Platform.select({
android: `${RNFS.DownloadDirectoryPath}/${task.saveName}`,
ios: `${RNFS.DocumentDirectoryPath}/${task.saveName}`,
});
const downloadTask = BackgroundDownloader.download({
url: task.url,
destination: savePath,
});
// 监听下载进度
downloadTask.progress((percent, bytesDownloaded, totalBytes) => {
const progress = Math.floor(percent * 100);
task.progress = progress;
this.taskStatusMap[task.id].progress = progress;
if (this.onTaskProgress) {
this.onTaskProgress(task.id, progress);
}
});
// 下载完成回调
downloadTask.done(() => {
task.status = 'done';
this.taskStatusMap[task.id].status = 'done';
this.downloadingCount--;
this.checkAndStartNextTask();
});
// 下载失败回调
downloadTask.error((error) => {
console.log(`文件${task.saveName}下载失败:`, error);
task.status = 'failed';
this.taskStatusMap[task.id].status = 'failed';
this.downloadingCount--;
this.checkAndStartNextTask();
});
}
// 取消所有下载任务
cancelAllDownload() {
this.taskQueue = [];
this.downloadingCount = 0;
this.taskStatusMap = {};
BackgroundDownloader.cancelAll();
}
}
export default new PdfBatchDownloader();
组件中使用下载管理器
在业务组件中引入下载管理器,调用对应方法即可实现批量下载:
import React, {useState} from 'react';
import {View, Button, Text, FlatList} from 'react-native';
import PdfBatchDownloader from './PdfBatchDownloader';
const pdfList = [
{id: '1', url: 'https://ipipp.com/pdf1.pdf', saveName: '文档1.pdf'},
{id: '2', url: 'https://ipipp.com/pdf2.pdf', saveName: '文档2.pdf'},
{id: '3', url: 'https://ipipp.com/pdf3.pdf', saveName: '文档3.pdf'},
{id: '4', url: 'https://ipipp.com/pdf4.pdf', saveName: '文档4.pdf'},
];
const BatchDownloadDemo = () => {
const [progressMap, setProgressMap] = useState({});
const handleStartDownload = () => {
PdfBatchDownloader.setDownloadList(pdfList);
PdfBatchDownloader.onTaskProgress = (id, progress) => {
setProgressMap(prev => ({...prev, [id]: progress}));
};
PdfBatchDownloader.onAllComplete = () => {
console.log('所有PDF文件下载完成');
};
PdfBatchDownloader.startBatchDownload();
};
return (
<View>
<Button title="开始批量下载PDF" onPress={handleStartDownload} />
<FlatList
data={pdfList}
keyExtractor={item => item.id}
renderItem={({item}) => (
<View>
<Text>{item.saveName}</Text>
<Text>下载进度: {progressMap[item.id] || 0}%</Text>
</View>
)}
/>
</View>
);
};
export default BatchDownloadDemo;
常见问题与优化方案
- 下载失败重试:可以在任务状态为failed时,增加重试机制,最多重试3次,避免临时网络问题导致下载失败。
- 断点续传:使用支持断点续传的下载库,记录已下载的字节数,网络中断后重新下载时从上次停止的位置继续。
- 存储空间检查:开始下载前检查设备剩余存储空间,避免下载过程中因空间不足导致失败。
- 后台下载支持:使用支持后台下载的库,即使应用退到后台,下载任务依然可以继续执行。
文件存储路径说明
不同平台的默认存储路径不同,上述代码中已经做了平台适配:
| 平台 | 默认存储路径 | 说明 |
|---|---|---|
| Android | Download目录 | 用户可以直接在系统文件管理器中找到下载的PDF文件 |
| iOS | 应用沙盒Document目录 | 文件仅当前应用可访问,需要额外做文件分享功能才能导出 |
如果需要自定义存储路径,只需要修改savePath的生成逻辑即可,确保路径有写入权限。
React_Native批量下载PDF文件文件管理修改时间:2026-07-13 08:27:15