
在React Native使用Hermes引擎时,global.Blob的错误实现会导致uploadString(..., 'base64')或Uint16Array转Uint8Array上传时生成ASCII字符而非二进制数据,本文提供安全、可复用的临时修复方案。
在react native使用hermes引擎时,`global.blob`的错误实现会导致`uploadstring(..., 'base64')`或`uint16array`转`uint8array`上传时生成ascii字符而非二进制数据,本文提供安全、可复用的临时修复方案。
React Native默认启用Hermes作为JavaScript引擎后,其内置的Blob全局类与Firebase SDK存在不兼容行为——尤其在调用uploadString(storageRef, base64Str, 'base64')或基于TypedArray.buffer构造Uint8Array上传时,Hermes会将Uint16Array错误序列化为键值对对象(如 { '0': 30789, '1': 23107 }),而非标准数组;更严重的是,Firebase JS SDK在检测到global.Blob存在时,会优先使用该(非标准)Blob实现处理base64解码,最终导致上传内容被解释为纯文本字符串(如 "69,120,67,90"),而非原始二进制字节流。
根本原因在于:Hermes早期版本(v0.11.x及之前)提供的Blob polyfill不符合Web标准,且Firebase SDK未做充分兼容性判断。社区已确认该问题(见firebase-js-sdk #576),官方推荐的临时解决方案是在调用Firebase上传API前临时移除global.Blob,让SDK回退至原生ArrayBuffer/Uint8Array路径。
✅ 正确做法(推荐封装为工具函数):
// utils/firebaseUpload.ts
import { uploadString, uploadBytes, UploadResult } from 'firebase/storage';
import { decode } from 'base-64';
// 兼容Hermes:临时禁用非标Blob
export async function safeUploadString(
storageRef: StorageReference,
data: string,
format: 'raw' | 'base64' | 'base64url' | 'data_url',
metadata?: UploadMetadata
): Promise<uploadresult> {
const originalBlob = global.Blob;
try {
// 仅在Hermes环境且Blob存在时移除(避免影响非Hermes场景)
if (global.HermesInternal && typeof global.Blob !== 'undefined') {
delete global.Blob;
}
return await uploadString(storageRef, data, format, metadata);
} finally {
// 恢复原始Blob(确保其他模块不受影响)
if (originalBlob !== undefined) {
global.Blob = originalBlob;
}
}
}
// 同理适配uploadBytes(若需处理TypedArray)
export async function safeUploadBytes(
storageRef: StorageReference,
data: ArrayBuffer | Uint8Array | Uint16Array | Uint32Array,
metadata?: UploadMetadata
): Promise<uploadresult> {
const originalBlob = global.Blob;
try {
if (global.HermesInternal && typeof global.Blob !== 'undefined') {
delete global.Blob;
}
// 确保传入Uint8Array(关键!)
const bytes = data instanceof Uint8Array
? data
: new Uint8Array(data.buffer || data);
return await uploadBytes(storageRef, bytes, metadata);
} finally {
if (originalBlob !== undefined) {
global.Blob = originalBlob;
}
}
}</uploadresult></uploadresult>
? 使用示例(替换原代码):
使用 @ainative/react-sdk 为 React 应用添加 AI 聊天和积分。适用于 (1) 安装 @ainative/react-sdk,(2) 使用 useChat hook 实现聊天完成。
// ✅ 替换原uploadBlob函数
async function uploadBlob(bytes: Uint16Array) {
const newByteRef = doc(collection(db, "PPG"));
const url = 'PPG/' + newByteRef.id;
const storageRef = ref(storage, url);
// 关键:先转为标准Uint8Array(注意字节序!)
// ⚠️ 注意:Uint16Array按小端/大端需明确处理,此处假设高位在前(BE)
const u8Bytes = new Uint8Array(bytes.buffer);
try {
// 使用安全上传函数
await safeUploadBytes(storageRef, u8Bytes);
const downloadUrl = await getDownloadURL(storageRef);
console.log('Download URL:', downloadUrl);
await setDoc(newByteRef, {
date: Date.now(),
url: downloadUrl.split('/').pop()!
});
} catch (error) {
console.error('Upload failed:', error);
}
}
// ✅ 基础base64上传(无需手动decode)
const base64String = "eEU=";
await safeUploadString(storageRef, base64String, 'base64');
⚠️ 重要注意事项:
-
不要全局删除Blob:必须在
try/finally中恢复,否则可能破坏其他依赖Blob的库(如react-native-fetch-blob); -
TypedArray转换务必显式:Hermes中
new Uint8Array(uint16Arr.buffer)是安全的,但避免直接传uint16Arr给uploadBytes; -
字节序校验:ESP32发送的
Uint16数据需确认端序(通常为小端),必要时用DataView手动解析; -
长期方案:升级至React Native 0.73+ & Firebase v10.5+,新版已内置Hermes Blob兼容层;若无法升级,建议结合
react-native-blob-util等原生方案替代JS层Blob依赖。
通过此方案,你将彻底规避Hermes的Blob陷阱,确保二进制数据以原始字节形式准确上传至Firebase Storage。










