You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动