React Native Expo下AWS Amplify文件离线上传实现方案咨询
实现AWS Amplify Storage离线文件同步到S3的方案(React Native Expo)
针对你的需求,目前JavaScript生态没有官方的Amplify Storage离线上传封装,但可以通过本地缓存+自定义同步队列、结合AppSync离线能力这两类思路实现,以下是具体落地方案:
思路一:本地缓存+自定义上传队列
核心逻辑是离线时将文件暂存本地,用持久化队列记录上传任务,联网后自动触发同步,完全自主控制流程。
1. 本地文件存储与队列持久化
使用Expo的FileSystem存储离线文件,用AsyncStorage或Realm维护上传队列(Realm更适合复杂数据结构):
import * as FileSystem from 'expo-file-system'; import AsyncStorage from '@react-native-async-storage/async-storage'; // 保存文件到本地缓存目录 const saveFileLocally = async (sourceUri, fileName) => { const cacheDir = `${FileSystem.documentDirectory}pending_uploads/`; await FileSystem.makeDirectoryAsync(cacheDir, { intermediates: true }); const localPath = `${cacheDir}${fileName}`; await FileSystem.copyAsync({ from: sourceUri, to: localPath }); return localPath; }; // 添加任务到上传队列 const addToUploadQueue = async (localPath, s3Key, fileMeta) => { const queue = JSON.parse(await AsyncStorage.getItem('upload_queue')) || []; queue.push({ localPath, s3Key, contentType: fileMeta.contentType, status: 'pending', retryCount: 0 }); await AsyncStorage.setItem('upload_queue', JSON.stringify(queue)); };
2. 监听网络状态触发同步
用Expo的Network或react-native-netinfo监听联网状态,一旦恢复连接就处理队列:
import * as Network from 'expo-network'; const startNetworkListener = () => { Network.addNetworkStateListener(async ({ isConnected }) => { if (isConnected) { await processUploadQueue(); } }); };
3. 处理上传队列(含重试逻辑)
遍历队列调用Amplify Storage上传,成功后清理本地文件和队列,失败则标记重试:
import { Storage } from 'aws-amplify'; const processUploadQueue = async () => { let queue = JSON.parse(await AsyncStorage.getItem('upload_queue')) || []; const updatedQueue = []; for (const task of queue) { try { // 上传到S3 await Storage.put(task.s3Key, task.localPath, { contentType: task.contentType }); // 上传成功,删除本地文件 await FileSystem.deleteAsync(task.localPath); } catch (err) { console.error(`上传失败: ${task.s3Key}`, err); // 重试不超过3次 if (task.retryCount < 3) { updatedQueue.push({ ...task, retryCount: task.retryCount + 1, status: 'failed' }); } else { // 多次失败标记为永久失败,供用户手动重试 updatedQueue.push({ ...task, status: 'permanent_failed' }); } } } await AsyncStorage.setItem('upload_queue', JSON.stringify(updatedQueue)); };
思路二:结合AppSync离线能力+S3预签名URL
利用AppSync的离线mutation队列特性,后端生成S3预签名URL,前端拿到后完成文件上传,复用DataStore的离线同步机制。
1. 定义GraphQL Schema与后端逻辑
在AppSync schema中新增文件上传相关类型与mutation:
type FileUpload { id: ID! fileName: String! contentType: String! s3Key: String! status: String! presignedUrl: String # 后端生成的预签名上传URL } type Mutation { createFileUpload(input: CreateFileUploadInput!): FileUpload markFileUploadComplete(input: MarkFileUploadCompleteInput!): FileUpload }
后端通过Lambda resolver处理createFileUpload请求,生成S3预签名URL并返回;处理markFileUploadComplete标记文件状态为已上传。
2. 前端离线提交与同步处理
离线时调用createFileUpload mutation,AppSync会将请求缓存到本地;联网后AppSync自动执行mutation,前端拿到预签名URL后完成上传:
import { API, graphqlOperation } from 'aws-amplify'; import * as mutations from './graphql/mutations'; import * as subscriptions from './graphql/subscriptions'; // 离线提交文件上传请求 const submitOfflineFileUpload = async (fileMeta) => { await API.graphql(graphqlOperation(mutations.createFileUpload, { input: { fileName: fileMeta.name, contentType: fileMeta.type, s3Key: `user_uploads/${Date.now()}_${fileMeta.name}`, status: 'PENDING' } })); }; // 监听AppSync同步事件,完成文件上传 const setupUploadSyncListener = () => { API.graphql(graphqlOperation(subscriptions.onCreateFileUpload)).subscribe({ next: async (data) => { const uploadTask = data.value.data.onCreateFileUpload; if (!uploadTask.presignedUrl) return; // 读取本地文件并上传到S3 const fileContent = await FileSystem.readAsStringAsync(uploadTask.localPath, { encoding: FileSystem.EncodingType.Base64 }); await fetch(uploadTask.presignedUrl, { method: 'PUT', body: fileContent, headers: { 'Content-Type': uploadTask.contentType } }); // 标记上传完成 await API.graphql(graphqlOperation(mutations.markFileUploadComplete, { input: { id: uploadTask.id, status: 'COMPLETED' } })); // 删除本地缓存文件 await FileSystem.deleteAsync(uploadTask.localPath); } }); };
关键注意事项
- 大文件处理:针对大文件可实现分块上传(调用S3的multipart upload API),避免单次上传失败重传全部内容。
- 权限配置:确保IAM策略允许当前Auth用户执行S3上传操作,可通过Amplify Storage的
level参数(private/public)控制权限。 - 队列清理:用户注销或卸载APP时,需清理本地缓存文件与上传队列,避免占用存储空间。
内容的提问来源于stack exchange,提问作者QIAN TANG
相关产品推荐
相关产品推荐

