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
相关产品推荐
相关产品推荐

