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
相关产品推荐
相关产品推荐

