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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 00:15:39