基于Flutter与AWS Amplify的离线媒体对象创建及云端同步实现问询
Flutter + AWS Amplify 离线创建带媒体对象并可靠同步方案
1. 离线时先存本地,不等网络
离线状态下不用死等S3返回Key,直接按以下步骤操作:
- 给Datastore模型新增两个字段:
mediaLocalPath(存储本地媒体文件路径)、uploadStatus(标记上传状态:待上传/已上传/上传失败),填充好这些字段后直接调用Amplify.DataStore.save()存入本地存储。 - 将图片/视频复制到应用专属的文档目录(用
path_provider获取路径),避免被系统自动清理。
2. 网络恢复后自动补传媒体
用connectivity_plus包监听网络状态,一旦检测到联网就批量处理待上传内容:
- 查询Datastore中所有
uploadStatus为pending的对象,取出对应的本地媒体路径。 - 调用Amplify Storage的上传接口,拿到S3的Key后,更新Datastore对象的
mediaS3Key字段,将uploadStatus改为completed,再重新保存对象。 - 上传成功后可删除本地缓存的媒体文件节省空间;若上传失败,将状态改为
failed,后续可添加重试逻辑。
3. 解决Datastore偶尔不同步的问题
- 检查模型Schema:确保
@model注解开启同步并配置冲突策略,比如@model(syncEnabled: true, conflictResolution: { strategy: "VERSIONED" }),让本地与云端的版本冲突能自动处理。 - 不依赖自动同步,网络恢复时主动调用
Amplify.DataStore.sync()强制触发同步。 - 用
Amplify.DataStore.observeQuery()监听同步状态,排查日志中的错误(比如字段不匹配、权限配置问题),定位偶尔不同步的根源。
4. 代码片段参考
GraphQL模型定义
type MediaObject @model @auth(rules: [{ allow: owner }]) { id: ID! title: String! mediaLocalPath: String # 离线时存储本地文件路径 mediaS3Key: String # 联网后存储S3文件Key uploadStatus: String! # 标记上传状态:pending/completed/failed }
离线创建对象代码
import 'package:path_provider/path_provider.dart'; import 'package:amplify_datastore/amplify_datastore.dart'; import 'package:uuid/uuid.dart'; Future<void> createOfflineMedia(String title, File mediaFile) async { // 获取应用缓存目录 final appDir = await getApplicationDocumentsDirectory(); // 生成唯一文件名避免重复 final localFilePath = '${appDir.path}/${const Uuid().v4()}_${mediaFile.path.split('/').last}'; // 复制媒体文件到缓存目录 await mediaFile.copy(localFilePath); // 创建本地对象并保存 final mediaObj = MediaObject( title: title, mediaLocalPath: localFilePath, uploadStatus: 'pending', ); await Amplify.DataStore.save(mediaObj); }
网络恢复同步代码
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:amplify_storage_s3/amplify_storage_s3.dart'; import 'package:amplify_datastore/amplify_datastore.dart'; import 'package:uuid/uuid.dart'; // 初始化网络监听 void setupNetworkSync() { Connectivity().onConnectivityChanged.listen((result) async { if (result != ConnectivityResult.none) { await syncPendingMedia(); } }); } // 同步待上传媒体 Future<void> syncPendingMedia() async { // 查询所有待上传对象 final pendingItems = await Amplify.DataStore.query(MediaObject.classType, where: MediaObject.UPLOADSTATUS.eq('pending') ); for (var item in pendingItems) { if (item.mediaLocalPath == null) continue; final localFile = File(item.mediaLocalPath!); if (!await localFile.exists()) continue; try { // 上传至S3 final uploadResult = await Amplify.Storage.uploadFile( local: localFile, key: 'user_media/${const Uuid().v4()}_${localFile.path.split('/').last}', ); // 更新对象信息 final updatedItem = item.copyWith( mediaS3Key: uploadResult.key, uploadStatus: 'completed', mediaLocalPath: null, // 可选:清除本地路径记录 ); await Amplify.DataStore.save(updatedItem); // 删除本地缓存文件 await localFile.delete(); } catch (e) { // 标记上传失败,后续可重试 final failedItem = item.copyWith(uploadStatus: 'failed'); await Amplify.DataStore.save(failedItem); } } // 手动触发Datastore同步 await Amplify.DataStore.sync(); }
额外提醒
- 给上传失败的对象添加重试次数限制,比如失败3次后通知用户手动重试,避免无限循环。
- 确保Amplify Auth的用户会话支持离线缓存,否则联网后可能因身份验证问题导致上传失败。
- 大文件建议使用分块上传,Amplify Storage原生支持该功能,离线时可缓存上传进度,下次联网后继续上传。
内容的提问来源于stack exchange,提问作者Bala
相关产品推荐
相关产品推荐

