Expo/React Native:TestFlight更新后expo-file-system的documentDirectory中图片丢失的原因及持久化方案咨询
Expo/React Native:TestFlight更新后expo-file-system的documentDirectory中图片丢失的原因及持久化方案咨询
兄弟,我来给你拆解这个问题,帮你搞懂为啥更新后图片没了,以及怎么解决这个头疼的事儿~
为啥TestFlight更新后图片会丢?
这事儿本质上和iOS的应用沙盒机制以及Expo的目录设计有关:
- iOS在处理TestFlight(包括App Store)应用更新时,会创建一个全新的应用沙盒容器,然后自动迁移旧容器里的核心持久化数据(比如AsyncStorage存在的
NSUserDefaults或者Expo专属存储区),但Expo的documentDirectory对应的是沙盒里的ExponentFileSystem子目录,这个目录在某些EAS Build配置或者iOS的更新逻辑中,并没有被纳入强制迁移的名单,所以更新过程中很容易被系统清理掉。 - 另外,如果你的EAS Build开启了应用缓存优化,iOS系统可能会把
documentDirectory下的文件判定为非必要缓存,在更新时直接清掉,而AsyncStorage因为是专门的键值对持久化存储,系统会明确保留这部分数据。
靠谱的持久化方案,让图片跨更新不丢失
我给你整理了几个实战验证过的方案,你可以根据自己的需求选:
1. 自定义专属子目录存储
这是最简单的方案,不需要改太多配置:
在documentDirectory下创建一个完全属于你的子目录(比如user_pics),把所有用户图片都存在这个目录里。iOS在更新时会完整迁移整个Documents目录(包括你自定义的子目录),不会随便清理。
代码示例大概是这样:
import * as FileSystem from 'expo-file-system'; import AsyncStorage from '@react-native-async-storage/async-storage'; // 定义专属目录路径 const USER_PICS_DIR = `${FileSystem.documentDirectory}user_pics/`; // 先确保目录存在,不存在就创建 const dirInfo = await FileSystem.getInfoAsync(USER_PICS_DIR); if (!dirInfo.exists) { await FileSystem.makeDirectoryAsync(USER_PICS_DIR, { intermediates: true }); } // 复制选中的图片到这个目录,注意给文件起个唯一的名字(比如用时间戳+随机数) const uniqueFileName = `${Date.now()}_${Math.random().toString(36).slice(2)}.jpg`; await FileSystem.copyAsync({ from: selectedImageUri, to: `${USER_PICS_DIR}${uniqueFileName}` }); // 最后把这个新URI存在AsyncStorage里 await AsyncStorage.setItem('user_pic_uri', `${USER_PICS_DIR}${uniqueFileName}`);
2. 使用App Groups共享容器
如果想要更稳妥的持久化(甚至卸载重装后还能保留,只要是同一个开发者的应用),可以配置App Groups:
- 先在你的EAS Build配置文件
eas.json里添加App Groups权限,比如:
注意把"build": { "production": { "ios": { "entitlements": { "com.apple.security.application-groups": ["group.your.bundle.identifier"] } } } }group.your.bundle.identifier换成你自己的应用组ID,格式是group.+ 你的应用bundle ID。 - 然后用Expo的
FileSystem.getBundleGroupDirectoryAsync获取共享容器的路径,把图片存在这里:
这个共享容器不属于单个应用沙盒,所以更新、甚至卸载重装同开发者的应用,数据都不会丢。import * as FileSystem from 'expo-file-system'; const groupDir = await FileSystem.getBundleGroupDirectoryAsync('group.your.bundle.identifier'); const targetPath = `${groupDir}user_pics/`; // 后续的创建目录、复制图片逻辑和上面一致
3. 可选:同步到iCloud(适合需要跨设备同步的场景)
如果用户允许,你可以把图片同步到iCloud云盘,这样即使应用更新或重装,都能从iCloud拉回图片。不过要注意用户的iCloud存储空间限制,以及需要申请iCloud相关的权限,在app.json里配置:
"ios": { "infoPlist": { "NSUbiquitousContainers": { "iCloud.your.bundle.identifier": { "NSUbiquitousContainerIsDocumentScopePublic": true, "NSUbiquitousContainerName": "Your App Name", "NSUbiquitousContainerSupportedFolderLevels": "Any" } } } }
然后用FileSystem.uploadAsync把图片传到iCloud,后续需要时再下载回来。
额外的注意事项
- 每次应用启动后,记得检查保存的图片URI对应的文件是否存在,用
FileSystem.getInfoAsync(uri)来判断,如果文件不存在,可以提示用户重新上传,或者从备份(比如iCloud)恢复。 - 定期清理用户不再需要的图片,既节省用户的存储空间,也能减少更新时的数据迁移量,避免出问题。
内容来源于stack exchange




