React Native Expo Base64图片任意方法均无法上传至Firebase Storage
你遇到的所有报错本质是三个核心问题:全局polyfill写法不兼容RN JS引擎导致崩溃、data URI拼接错误触发Firebase格式校验、RN原生网络栈不支持解析data协议URI导致fetch/XHR转Blob失败。下面是经过验证的可运行方案,按步骤来就行。
前置修正:先处理基础数据错误
你之前展示图片用的URI拼接少了关键的逗号,这是格式报错的核心诱因之一,Firebase对上传字符串的格式校验非常严格,容错率远低于Image组件。
首先清洗expo-camera返回的base64数据,部分机型返回的base64会夹带换行、空格等非法字符:
// 拍照配置不需要改 const photo = await camera.takePictureAsync({ quality: 1, base64: true, exif: false }) // 清洗base64,移除所有空白字符 const pureBase64 = photo.base64.replace(/\s/g, '') // 拼接完整data URL,注意base64后面必须加逗号 const dataUrl = `data:image/jpeg;base64,${pureBase64}`
方案一:用uploadString上传(最稳定,代码量最少,优先选)
不要安装第三方base-64包做全局polyfill,第三方包和RN的Hermes引擎兼容性很差,很容易直接崩溃。Firebase依赖的abort-controller包Expo环境默认自带,直接用里面的atob/btoa做polyfill即可,不会有兼容问题。
在项目入口文件(App.js/index.js)的最顶部添加以下polyfill代码,放在所有import的最前面:
// 不需要额外安装依赖,项目自带 import { atob, btoa } from 'abort-controller/dist/abort-controller' global.atob = atob global.btoa = btoa
上传逻辑直接用拼接好的完整dataUrl,格式参数指定为data_url,不要传纯base64字符串:
import { getStorage, ref, uploadString, getDownloadURL } from 'firebase/storage' const storage = getStorage() // 自定义存储路径,建议加时间戳避免重名 const storageRef = ref(storage, `camera_upload/${Date.now()}.jpg`) const snapshot = await uploadString(storageRef, dataUrl, 'data_url') const downloadUrl = await getDownloadURL(snapshot.ref) console.log('上传成功,图片访问地址:', downloadUrl)
方案二:用uploadBytes传Blob(如果业务必须用Blob格式)
不要用fetch、XMLHttpRequest去请求data://开头的URI,RN的网络栈不识别data协议,会把URI当成本地文件路径查找,必然报ENOENT找不到文件的错误。
如果必须传Blob,直接读拍照返回的本地临时文件(就是photo.uri字段,根本不需要用base64转,开base64反而会占用大量内存容易OOM),用expo-file-system做转换:
首先安装依赖(如果没装的话):
npx expo install expo-file-system
转换+上传代码:
import * as FileSystem from 'expo-file-system' import { getStorage, ref, uploadBytes, getDownloadURL } from 'firebase/storage' const storage = getStorage() const storageRef = ref(storage, `camera_upload/${Date.now()}.jpg`) // 直接读本地拍照临时文件,不需要走base64 const base64Data = await FileSystem.readAsStringAsync(photo.uri, { encoding: FileSystem.EncodingType.Base64 }) // 转Blob const blob = Uint8Array.from(atob(base64Data), c => c.charCodeAt(0)) // 上传 const snapshot = await uploadBytes(storageRef, blob, { contentType: 'image/jpeg' }) const downloadUrl = await getDownloadURL(snapshot.ref) console.log('上传成功,图片访问地址:', downloadUrl)
避坑清单
- 不要用第三方base-64包做全局polyfill,90%的崩溃问题都是这个导致的
- 拼接data URL时
base64,的逗号绝对不能漏,漏了必然报格式非法 - 拍照时如果不需要直接拿base64做业务处理,建议把
base64:true关掉,直接用photo.uri本地上传,内存占用能降80%以上,大幅减少崩溃概率 - 不要尝试用fetch/XHR请求data开头的URI,RN环境下这个路径走不通
- 上传时如果是jpg格式,contentType统一写
image/jpeg,不要写image/jpg,后者不是标准MIME类型,部分场景下会导致Firebase返回的图片无法直接预览
内容的提问来源于stack exchange,提问作者skar02

