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

Expo Go开发环境数据库可完整加载,TestFlight仅加载243字节求助

Expo生产构建(TestFlight)中SQLite数据库仅加载243字节问题排查与解决

问题背景

开发的Expo应用依赖assets文件夹内4.8MB的预加载数据库worldEnglishBible.db,通过expo-file-system将数据库复制到可访问目录供expo-sqlite使用。Expo Go测试时数据库可完整加载,但生产构建上传至TestFlight后,仅能加载243字节的不完整文件,导致无法正常访问数据。

现有实现代码

数据库加载逻辑

import * as FileSystem from "expo-file-system";
import { Asset } from "expo-asset";

const downloadWEB = async (setDatabaseSize, setConsoleText) => {
  const callback = (downloadProgress) => {
    console.log(downloadProgress.totalBytesWritten);
    setDatabaseSize(downloadProgress.totalBytesWritten);
  };

  const downloadResumableWEB = FileSystem.createDownloadResumable(
    Asset.fromModule(require("../assets/worldEnglishBible.db")).uri,
    FileSystem.documentDirectory + "SQLite/worldEnglishBible.db",
    {},
    callback
  );

  try {
    await FileSystem.makeDirectoryAsync(
      FileSystem.documentDirectory + "SQLite"
    );
    const { uri } = await downloadResumableWEB.downloadAsync();
    console.log("Finished downloading to ", uri);
    setConsoleText("Finished downloading to " + uri);
  } catch (error) {
    console.log(error.message);
    setConsoleText(error.message);
  }
};

const deleteDatabaseSQL = async (setDatabaseSize, setConsoleText) => {
  try {
    await FileSystem.deleteAsync(FileSystem.documentDirectory + "SQLite");
    console.log("database deleted");
    setConsoleText("database deleted");
    setDatabaseSize(0);
  } catch (error) {
    console.log(error.message);
    setConsoleText(error.message);
  }
};

export { downloadWEB, deleteDatabaseSQL };

Metro配置(metro.config.js)

const { getDefaultConfig } = require("expo/metro-config");

const config = getDefaultConfig(__dirname);

config.resolver.assetExts.push(
  // Adds support for `.db` files for SQLite databases
  "db"
);

module.exports = config;

解决方案

方案1:确保Asset预加载完成

生产构建中,Asset.fromModule返回的URI可能指向未完全就绪的资源,导致下载到不完整文件。需先调用Asset.loadAsync确保资源加载完成,同时优化目录创建逻辑避免重复报错:

import * as FileSystem from "expo-file-system";
import { Asset } from "expo-asset";

const downloadWEB = async (setDatabaseSize, setConsoleText) => {
  const callback = (downloadProgress) => {
    console.log(downloadProgress.totalBytesWritten);
    setDatabaseSize(downloadProgress.totalBytesWritten);
  };

  try {
    // 预加载Asset,确保资源可用
    const asset = Asset.fromModule(require("../assets/worldEnglishBible.db"));
    if (!asset.downloaded) {
      await asset.loadAsync();
    }

    // 创建目录时忽略已存在的情况
    await FileSystem.makeDirectoryAsync(
      FileSystem.documentDirectory + "SQLite",
      { exists: 'ignore' }
    );

    const downloadResumableWEB = FileSystem.createDownloadResumable(
      asset.uri,
      FileSystem.documentDirectory + "SQLite/worldEnglishBible.db",
      {},
      callback
    );

    const { uri } = await downloadResumableWEB.downloadAsync();
    console.log("Finished downloading to ", uri);
    setConsoleText("Finished downloading to " + uri);

    // 可选:验证文件完整性
    const fileInfo = await FileSystem.getInfoAsync(uri);
    console.log("下载后文件大小:", fileInfo.size);
    if (fileInfo.size < 4 * 1024 * 1024) {
      setConsoleText("数据库文件可能不完整");
    }
  } catch (error) {
    console.log(error.message);
    setConsoleText(error.message);
  }
};

方案2:改用FileSystem.copyAsync替代下载逻辑

Asset在生产构建中是打包到应用内的本地资源,直接复制比下载更可靠,避免下载逻辑带来的潜在问题:

import * as FileSystem from "expo-file-system";
import { Asset } from "expo-asset";

const downloadWEB = async (setDatabaseSize, setConsoleText) => {
  try {
    const asset = Asset.fromModule(require("../assets/worldEnglishBible.db"));
    if (!asset.downloaded) {
      await asset.loadAsync();
    }

    const targetDir = FileSystem.documentDirectory + "SQLite";
    const targetPath = `${targetDir}/worldEnglishBible.db`;

    // 检查目标文件是否已存在且完整,避免重复操作
    const targetInfo = await FileSystem.getInfoAsync(targetPath);
    if (targetInfo.exists && targetInfo.size > 4 * 1024 * 1024) {
      setConsoleText("数据库已存在且完整");
      setDatabaseSize(targetInfo.size);
      return;
    }

    await FileSystem.makeDirectoryAsync(targetDir, { exists: 'ignore' });
    // 直接复制Asset到目标路径
    await FileSystem.copyAsync({
      from: asset.uri,
      to: targetPath,
    });

    const finalInfo = await FileSystem.getInfoAsync(targetPath);
    console.log("复制后数据库大小:", finalInfo.size);
    setDatabaseSize(finalInfo.size);
    setConsoleText("数据库已复制到:" + targetPath);
  } catch (error) {
    console.log(error.message);
    setConsoleText(error.message);
  }
};

方案3:清理构建缓存确保资源正确打包

  • 运行expo r -c清除Metro开发缓存
  • 构建生产包时执行expo build:ios --clear-cache,确保db资源被正确打包进应用

问题根源

  1. Expo Go与生产构建的资源处理差异:Expo Go实时加载资源,而生产构建将资源打包到应用包内,未预加载的Asset可能返回临时占位路径,导致下载到不完整文件。
  2. 目录创建逻辑缺陷:原代码未处理目录已存在的情况,可能触发隐式错误影响后续操作。
  3. 下载逻辑不适用于本地资源:createDownloadResumable更适合远程资源下载,处理本地Asset时不如直接复制可靠。

内容的提问来源于stack exchange,提问作者Sam Sweetin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 09:20:33