React Native实现PDF与视频下载至文件管理器/下载文件夹方案求助
React Native 实现PDF/视频下载到文件管理器/下载文件夹方案
作为经常处理React Native文件操作的开发者,我推荐用react-native-fs(文件系统操作)和react-native-permissions(权限处理)这两个社区成熟的库来实现需求,下面是完整的步骤和可运行代码:
一、安装依赖
先把核心依赖装到位:
# 安装文件系统操作库 npm install react-native-fs --save # 安装权限处理库 npm install react-native-permissions --save # iOS 需执行pod安装 cd ios && pod install && cd ..
二、配置平台权限
Android 配置
在android/app/src/main/AndroidManifest.xml中添加必要权限:
<!-- Android 9及以下需要写入外部存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- Android 13+ 读取媒体文件权限(若需访问已下载文件) --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 网络权限(下载必须) --> <uses-permission android:name="android.permission.INTERNET" />
iOS 配置
在ios/YourProjectName/Info.plist中添加以下配置,让文件能被系统文件管理器识别:
<!-- 开启文件共享,允许文件应用访问Documents目录 --> <key>UIFileSharingEnabled</key> <true/> <!-- 允许直接在应用内打开文件 --> <key>LSSupportsOpeningDocumentsInPlace</key> <true/> <!-- 权限说明(向用户解释为什么需要访问文件) --> <key>NSPhotoLibraryAddUsageDescription</key> <string>需要保存下载的PDF/视频到您的设备</string>
三、完整下载功能代码
下面是一个可复用的下载组件,支持PDF和视频,自动保存到系统可访问的下载目录(Android为公共Download文件夹,iOS为Documents目录):
import React, { useState } from 'react'; import { View, Button, Text, StyleSheet, Platform } from 'react-native'; import RNFS from 'react-native-fs'; import { check, request, PERMISSIONS, RESULTS } from 'react-native-permissions'; const DownloadFileComponent = ({ fileUrl, fileName }) => { const [downloadProgress, setDownloadProgress] = useState(0); const [isDownloading, setIsDownloading] = useState(false); const [downloadStatus, setDownloadStatus] = useState(''); // 检查并请求权限(Android专属,iOS无需额外权限) const checkPermissions = async () => { if (Platform.OS === 'android') { const permission = Platform.Version >= 29 ? PERMISSIONS.ANDROID.READ_MEDIA_VIDEO // Android 13+ 权限 : PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE; // Android 9及以下权限 const result = await check(permission); if (result === RESULTS.DENIED) { const requestResult = await request(permission); return requestResult === RESULTS.GRANTED; } return result === RESULTS.GRANTED; } return true; // iOS默认允许访问Documents目录 }; const startDownload = async () => { setIsDownloading(true); setDownloadStatus('开始下载...'); // 先校验权限 const hasPermission = await checkPermissions(); if (!hasPermission) { setDownloadStatus('权限被拒绝,无法完成下载'); setIsDownloading(false); return; } // 获取系统可访问的下载目录路径 const downloadPath = Platform.OS === 'android' ? RNFS.DownloadDirectoryPath : RNFS.DocumentDirectoryPath; const fullPath = `${downloadPath}/${fileName}`; try { // 检查文件是否已存在,避免重复下载 const fileExists = await RNFS.exists(fullPath); if (fileExists) { setDownloadStatus('文件已存在于下载文件夹'); setIsDownloading(false); return; } // 发起下载请求 const downloadTask = RNFS.downloadFile({ fromUrl: fileUrl, toFile: fullPath, progressInterval: 100, // 每100ms更新一次进度 progress: (res) => { const progress = (res.bytesWritten / res.contentLength) * 100; setDownloadProgress(Math.round(progress)); setDownloadStatus(`下载中:${progress.toFixed(0)}%`); }, }); // 监听下载结果 downloadTask.promise.then((res) => { if (res.statusCode === 200) { setDownloadStatus('下载完成!文件已保存到下载文件夹'); setDownloadProgress(100); } else { setDownloadStatus('下载失败:服务器返回异常'); } }).catch((err) => { setDownloadStatus(`下载失败:${err.message}`); }).finally(() => { setIsDownloading(false); }); } catch (err) { setDownloadStatus(`下载异常:${err.message}`); setIsDownloading(false); } }; return ( <View style={styles.container}> <Button title={isDownloading ? '下载中...' : '开始下载'} onPress={startDownload} disabled={isDownloading} /> {downloadStatus !== '' && ( <Text style={styles.statusText}>{downloadStatus}</Text> )} {downloadProgress > 0 && ( <View style={styles.progressBarContainer}> <View style={[ styles.progressBar, { width: `${downloadProgress}%` } ]} /> </View> )} </View> ); }; const styles = StyleSheet.create({ container: { padding: 20, alignItems: 'center', }, statusText: { marginTop: 15, fontSize: 14, color: '#333', }, progressBarContainer: { width: '100%', height: 8, backgroundColor: '#eee', marginTop: 15, borderRadius: 4, }, progressBar: { height: '100%', backgroundColor: '#2196F3', borderRadius: 4, }, }); export default DownloadFileComponent;
四、组件使用示例
在你的页面中直接引入,传入文件URL和自定义文件名即可:
import DownloadFileComponent from './DownloadFileComponent'; const App = () => { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <DownloadFileComponent fileUrl="https://example.com/sample.pdf" fileName="我的文档.pdf" /> <DownloadFileComponent fileUrl="https://example.com/sample.mp4" fileName="我的视频.mp4" /> </View> ); };
五、关键注意事项
- Android 10+ 适配:Android 10开始使用分区存储,
RNFS.DownloadDirectoryPath指向系统公共Download目录,无需额外权限即可写入,文件管理器直接可见。 - iOS 文件访问:开启
UIFileSharingEnabled后,用户可通过「文件」应用的「我的iPhone」->「你的应用名称」找到下载的文件。 - 错误处理:代码包含权限检查、文件存在性校验、网络错误捕获,覆盖了大部分常见异常场景。
- 进度优化:通过
progressInterval控制进度更新频率,避免UI频繁刷新影响性能。
内容的提问来源于stack exchange,提问作者Vicky Rao
相关产品推荐
相关产品推荐

