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

React Native带Token鉴权的API媒体文件本地存储方案求更佳实现

优化React Native中带Token授权的API文件存储方案

原实现方案

import RNFS from "react-native-fs";

export const appDirectory = `${RNFS.DocumentDirectoryPath}/EasyMd`;

export async function createAppDirectory() {
  try {
    const directoryExists = await RNFS.exists(appDirectory);
    if (!directoryExists) {
      await RNFS.mkdir(appDirectory);
    }
  } catch (error) {
    console.log("Error while creating app directory: ", error);
  }
}

async function getPdfFilePrescriptionDetails() {
  try {
    setPdfLoading(true);
    await createAppDirectory();
    const path = `${appDirectory}/prescription.pdf`;
    try {
      const downloadOptions = {
        fromUrl: `${Development.BASE_URL}${Api.GET_PRESCRIPTION_API}/${id}/export`,
        toFile: `${path}`,
        headers: {
          Accept: "application/json",
          Authorization: `Bearer ${token}`,
        },
      };
      await Promise.all([RNFS.downloadFile(downloadOptions)]);
      setPdfUri({ url: `file://${path}` });
    } catch (error) {
      console.log("Error saving PDF:", error);
    } finally {
      setPdfLoading(false);
    }
  } catch (e) {
    console.log("Errro v", e);
  }
}

优化后的实现方案

以下是针对原方案的关键优化点,兼顾可靠性、可维护性和用户体验:

1. 简化目录创建逻辑

原方案先判断目录存在性再创建,可直接利用RNFS.mkdir的recursive参数递归创建目录(自动处理父目录不存在的情况),避免冗余判断与竞态问题:

import RNFS from "react-native-fs";

// 用RNFS路径拼接方法,避免手动拼接斜杠的跨平台问题
export const appDirectory = RNFS.join(RNFS.DocumentDirectoryPath, "EasyMd");

export async function createAppDirectory() {
  try {
    await RNFS.mkdir(appDirectory, { recursive: true });
  } catch (error) {
    console.error("创建应用存储目录失败:", error.message);
    // 可添加用户可见提示,比如Toast.show("存储目录创建失败,请检查权限")
  }
}

2. 修复下载逻辑的潜在问题

原代码存在两处错误:Promise.all包裹单个任务无意义;RNFS.downloadFile需调用.promise才能等待下载完成。同时优化文件覆盖逻辑与请求头:

async function getPdfFilePrescriptionDetails() {
  setPdfLoading(true);
  try {
    await createAppDirectory();
    const path = RNFS.join(appDirectory, "prescription.pdf");

    // 可选:避免覆盖旧文件,添加时间戳重命名
    // const path = RNFS.join(appDirectory, `prescription_${Date.now()}.pdf`);

    // 如需覆盖旧文件,先删除已存在的文件
    if (await RNFS.exists(path)) {
      await RNFS.unlink(path);
    }

    const downloadOptions = {
      fromUrl: `${Development.BASE_URL}${Api.GET_PRESCRIPTION_API}/${id}/export`,
      toFile: path,
      headers: {
        Accept: "application/pdf", // 下载PDF需对应正确的MIME类型
        Authorization: `Bearer ${token}`,
      },
    };

    // 正确等待下载完成并获取结果
    const downloadResult = await RNFS.downloadFile(downloadOptions).promise;
    
    // 校验请求状态码,确保下载成功
    if (downloadResult.statusCode === 200) {
      setPdfUri({ url: `file://${path}` });
    } else {
      throw new Error(`下载失败,状态码:${downloadResult.statusCode}`);
    }
  } catch (error) {
    console.error("PDF下载/存储失败:", error.message);
    // 添加用户可见的错误提示,提升体验
    // Toast.show(`PDF下载失败:${error.message}`);
  } finally {
    setPdfLoading(false);
  }
}

3. 避免组件卸载后的状态更新(防止内存泄漏)

若函数位于函数组件内,添加isMounted判断,防止组件卸载后更新状态引发内存泄漏:

import { useRef, useEffect } from 'react';

function YourComponent() {
  const isMounted = useRef(true);

  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  async function getPdfFilePrescriptionDetails() {
    setPdfLoading(true);
    try {
      // ... 其他逻辑 ...
      if (downloadResult.statusCode === 200 && isMounted.current) {
        setPdfUri({ url: `file://${path}` });
      }
    } catch (error) {
      // ... 错误处理 ...
    } finally {
      if (isMounted.current) {
        setPdfLoading(false);
      }
    }
  }

  // ... 组件其他代码 ...
}

4. 可选:使用更简洁的现代文件API

若项目使用React Native 0.60+,可选择expo-file-system(非Expo项目也可单独安装),API更简洁且跨平台兼容性更好:

import * as FileSystem from 'expo-file-system';

export const appDirectory = `${FileSystem.documentDirectory}EasyMd`;

export async function createAppDirectory() {
  await FileSystem.makeDirectoryAsync(appDirectory, { intermediates: true });
}

async function getPdfFilePrescriptionDetails() {
  setPdfLoading(true);
  try {
    await createAppDirectory();
    const path = `${appDirectory}/prescription.pdf`;

    const downloadResult = await FileSystem.downloadAsync(
      `${Development.BASE_URL}${Api.GET_PRESCRIPTION_API}/${id}/export`,
      path,
      {
        headers: {
          Accept: "application/pdf",
          Authorization: `Bearer ${token}`,
        },
      }
    );

    if (downloadResult.status === 200) {
      setPdfUri({ url: downloadResult.uri });
    } else {
      throw new Error(`下载失败,状态码:${downloadResult.status}`);
    }
  } catch (error) {
    console.error("PDF下载失败:", error.message);
  } finally {
    setPdfLoading(false);
  }
}

内容的提问来源于stack exchange,提问作者Mujeeb Ur Rehman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 11:04:59