You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:14:59