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

React Native上传至AWS S3预签名URL时出现空文件问题

解决Expo React Native上传AWS S3文件为空的问题

我看你现在卡在上传后文件为空的问题上,全程还没报错,这确实挺让人头疼的。结合你的代码和Expo的特性,我给你几个关键的排查点和修复方案:

1. 别手动设置multipart/form-data请求头

你在fetch里手动加了'Content-Type': 'multipart/form-data;',这会导致fetch无法自动生成正确的boundary分隔符,服务器没法正确解析FormData里的内容,最终导致上传的文件是空的。直接删掉这个headers配置就行,fetch会自动处理。

2. 正确处理Expo Image Picker的文件(别用base64转Buffer)

React Native的FormData和web端的实现有差异,直接把base64转成Buffer传进去大概率会出问题。Expo Image Picker返回的是文件的uri,你应该用这个uri来创建符合要求的FormData条目,下面给你两种可行方案:

方案A:用FileSystem读取uri转Blob

先安装依赖(如果没装的话):

npx expo install expo-file-system

然后修改上传函数:

import * as FileSystem from 'expo-file-system';

export default async function uploadImage(
  fileUri: string, // 直接传Image Picker返回的uri
  filename: string,
  mimeType: string
) {
  const uploadData = await getUploadUrl(filename, mimeType);
  const data = new FormData();
  
  // 附加S3预签名的字段
  for (const [key, value] of Object.entries(uploadData.fields)) {
    data.append(key, value as string);
  }
  
  // 从uri创建Blob并附加到FormData
  const fileBlob = await FileSystem.readAsStringAsync(fileUri, {
    encoding: FileSystem.EncodingType.Base64,
  }).then(base64 => {
    return Blob([Uint8Array.from(atob(base64), c => c.charCodeAt(0))], { type: mimeType });
  });
  
  data.append('file', fileBlob, filename);
  
  let res = await fetch(uploadData.url, {
    method: 'post',
    body: data,
    // 这里不要加Content-Type头!
  });
  
  // 别忘了检查响应状态,方便排查问题
  if (!res.ok) {
    const errText = await res.text();
    throw new Error(`上传失败: ${errText}`);
  }
  
  return res;
}

方案B:直接用uri在FormData里append(更简单)

React Native的FormData支持直接传入uri对象,不需要转Blob:

export default async function uploadImage(
  fileUri: string,
  filename: string,
  mimeType: string
) {
  const uploadData = await getUploadUrl(filename, mimeType);
  const data = new FormData();
  
  for (const [key, value] of Object.entries(uploadData.fields)) {
    data.append(key, value as string);
  }
  
  // 直接传入uri、文件名和类型
  data.append('file', {
    uri: fileUri,
    name: filename,
    type: mimeType,
  } as any); // 加as any是因为TypeScript类型定义可能存在兼容问题
  
  let res = await fetch(uploadData.url, {
    method: 'post',
    body: data,
    // 同样不要加Content-Type头
  });
  
  if (!res.ok) {
    const errText = await res.text();
    throw new Error(`上传失败: ${errText}`);
  }
  
  return res;
}

3. 检查预签名URL的生成是否正确

另外,你要确认后端生成的预签名URL(getUploadUrl返回的内容)里:

  • fields中的Content-Type和你上传的文件类型完全一致,比如image/jpeg不能写成image/jpg
  • 预签名的权限是正确的(拥有put权限)
  • 文件名和你实际上传的文件名匹配

4. 调试小技巧

可以在上传前尝试打印FormData的内容(React Native里直接打印看不到详细内容,但可以用第三方库比如form-data的toString方法),或者在后端日志里查看接收的请求体,确认文件是否真的被传过去了。

按上面的方法改完,应该就能解决文件为空的问题了,我之前在Expo项目里遇到过几乎一样的情况,就是手动设置Content-Type和base64处理不当导致的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 15:27:34