React Native中Realm文件上传后无法读取数据的求助
React Native中Realm文件导出后无法读取数据的问题
问题背景
在React Native环境下,Realm官方没有提供现成的数据导入导出方法,我已成功导出.realm文件,但上传后无法读取其中的数据,以下是相关代码及运行日志,请求协助解决。
导出函数代码
import Realm from 'realm'; import RNFS from 'react-native-fs'; import {getRealm} from './realmService'; export const exportRealmData = async () => { try { const realm: Realm = await getRealm(); const exportFileName = 'qqqq.realm'; const exportFilePath = `${RNFS.DocumentDirectoryPath}/${exportFileName}`; if (await RNFS.exists(exportFilePath)) { await RNFS.unlink(exportFilePath); } const config = { path: exportFilePath, schema: realm.schema, schemaVersion: realm.schemaVersion + 1, encryptionKey: realm.encryptionKey, }; realm.writeCopyTo(config); const downloadsFolder = RNFS.DownloadDirectoryPath; const destinationFilePath = `${downloadsFolder}/${exportFileName}`; await RNFS.moveFile(exportFilePath, destinationFilePath); console.log('Realm data exported to', destinationFilePath); return destinationFilePath; } catch (error) { console.error('Error exporting Realm data', error); } };
上传与读取代码
function normalizePath(path: string | undefined) { try { if (path === undefined) { throw console.error('undefined'); } if (Platform.OS === 'ios' || Platform.OS === 'android') { const filePrefix = 'file:'; if (path.startsWith(filePrefix)) { path = path.substring(filePrefix.length); } path = decodeURIComponent(path); return path; } } catch (e) { console.error({msg: 'Failed to normalize path', data: e}); } } const handleFilePick = async () => { try { const result = await DocumentPicker.pick({ type: [DocumentPicker.types.allFiles], }); console.log(result); const {0: file1} = result; const path = normalizePath(file1?.uri); console.log(path); setRealmFile(path); } catch (err) { console.error(err); } }; console.log(realmFile); const handleDataSubmit = async () => { try { if (!realmFile) { console.error('Please upload a .realm file'); return; } const realm = await getRealm(); const existingRealm = await Realm.open({path: realmFile.uri}); console.log('This is the existing realm:', existingRealm); realm.write(() => { [User, Category, Expense, Currency, Debtor, Debt].forEach(schema => { const objects = existingRealm.objects(schema.schema.name); console.log(`Data for ${schema.schema.name}:`, objects.toJSON()); objects.forEach(object => { realm.create(schema.schema.name, object, Realm.UpdateMode.Modified); }); }); }); await AsyncStorageService.setItem('isOnboarded', JSON.stringify(true)); dispatch(setIsOnboarded(true)); console.log('Data updated successfully!'); } catch (error) { console.error('Error updating data:', error); } };
运行日志
LOG content://com.android.providers.downloads.documents/document/msf:1000000035 LOG [{"fileCopyUri": null, "name": "qqqq.realm", "size": 4848, "type": "application/octet-stream", "uri": "content://com.android.providers.downloads.documents/document/msf%3A1000000035"}] LOG content://com.android.providers.downloads.documents/document/msf:1000000035 LOG This is the existing realm: {"beforeNotifyListeners": {"eventType": "beforenotify", "listeners": Set {}, "realm": [Circular]}, "changeListeners": {"eventType": "change", "listeners": Set {}, "realm": [Circular]}, "schemaExtras": {}, "schemaListeners": {"eventType": "schema", "listeners": Set {}, "realm": [Circular]}, "syncSession": null}
问题分析
- 路径不兼容:日志显示获取到的是Android的
content://内容URI,但Realm的Realm.open()只能识别本地文件系统的绝对路径,无法直接解析内容URI,这是无法读取数据的核心原因。 - Schema版本不一致:导出时设置了
schemaVersion: realm.schemaVersion + 1,导致导出的Realm版本比原库高,导入时如果本地Schema版本不匹配,会出现数据读取异常。 - 变量引用错误:
handleDataSubmit中错误使用了realmFile.uri,但setRealmFile(path)存储的是处理后的路径字符串,并非带uri属性的对象。 - 加密密钥缺失(如果有加密):如果原Realm使用了加密,打开导入的文件时未传入相同的
encryptionKey,会导致无法解密打开。
解决方案
1. 转换内容URI为本地绝对路径
使用RNFS将content://指向的文件复制到App可访问的目录(如DocumentDirectoryPath),再用新路径打开Realm:
// 在handleFilePick中修改路径处理逻辑 const handleFilePick = async () => { try { const result = await DocumentPicker.pick({ type: [DocumentPicker.types.allFiles], }); const file1 = result[0]; // 复制文件到App私有目录 const destPath = `${RNFS.DocumentDirectoryPath}/${file1.name}`; // 对于Android content URI,RNFS.copyFile需要用content://路径作为源 await RNFS.copyFile(file1.uri, destPath); setRealmFile(destPath); console.log('文件已复制到:', destPath); } catch (err) { console.error(err); } };
2. 修正导出时的Schema版本
导出时保持和原Realm相同的版本号,避免版本冲突:
// 导出函数中的config修改 const config = { path: exportFilePath, schema: realm.schema, schemaVersion: realm.schemaVersion, // 去掉+1,保持版本一致 encryptionKey: realm.encryptionKey, };
3. 修正变量引用错误
在handleDataSubmit中直接使用realmFile作为路径,同时传入加密密钥(如果原库加密):
const handleDataSubmit = async () => { try { if (!realmFile) { console.error('请上传.realm文件'); return; } const realm = await getRealm(); // 获取原Realm的加密密钥(如果有) const originalRealm = await getRealm(); const existingRealm = await Realm.open({ path: realmFile, schema: realm.schema, // 确保Schema一致 encryptionKey: originalRealm.encryptionKey, // 原库加密时必须传入 }); console.log('已打开导入的Realm:', existingRealm); realm.write(() => { [User, Category, Expense, Currency, Debtor, Debt].forEach(schema => { const objects = existingRealm.objects(schema.schema.name); console.log(`${schema.schema.name}的数据:`, objects.toJSON()); objects.forEach(object => { realm.create(schema.schema.name, object, Realm.UpdateMode.Modified); }); }); }); await AsyncStorageService.setItem('isOnboarded', JSON.stringify(true)); dispatch(setIsOnboarded(true)); console.log('数据更新成功!'); } catch (error) { console.error('更新数据出错:', error); } };
4. 验证文件完整性
确保导出的.realm文件没有损坏,可以用Realm Studio打开导出的文件,确认数据存在,排除文件本身的问题。
内容的提问来源于stack exchange,提问作者Indranil Bhuin
相关产品推荐
相关产品推荐

