Skip to content

Commit 8eb4ae1

Browse files
committed
fix(storage, other): work around Hermes Blob / firebase-js-sdk mal-interaction
Hermes Blob implementation is lacking, and breaks storage uploads done using firebase-js-sdk which expects a full-featured implementation if "Blob" exists on globalThis if you temporarily hide the Blob implementation firebase-js-sdk will fallback to a different data upload method, then binary storage uploads will work So, make a custom binary upload pathway for storage when using firebase-js-sdk and hide Blob in it, but only if platform is not in fact web
1 parent 734019b commit 8eb4ae1

4 files changed

Lines changed: 197 additions & 111 deletions

File tree

packages/storage/e2e/StorageTask.e2e.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,16 +1076,11 @@ describe('storage() -> StorageTask', function () {
10761076
const jsonDerulo = JSON.stringify({ foo: 'bar' });
10771077
const expectedByteLength = jsonDerulo.length;
10781078

1079-
const arrayBuffer = new ArrayBuffer(jsonDerulo.length);
1080-
const arrayBufferView = new Uint8Array(arrayBuffer);
1081-
1082-
for (let i = 0, strLen = jsonDerulo.length; i < strLen; i++) {
1083-
arrayBufferView[i] = jsonDerulo.charCodeAt(i);
1084-
}
1079+
const jsonDeruloBytes = new Uint8Array([...jsonDerulo].map(char => char.charCodeAt(0)));
10851080

10861081
const uploadResult = await uploadBytes(
10871082
ref(getStorage(), `${PATH}/uploadBytesModular.json`),
1088-
arrayBuffer,
1083+
jsonDeruloBytes,
10891084
{
10901085
contentType: 'application/json',
10911086
},
@@ -1100,7 +1095,11 @@ describe('storage() -> StorageTask', function () {
11001095
it('rejects when metadata is not an object', async function () {
11011096
const { getStorage, ref, uploadBytes } = storageModular;
11021097
try {
1103-
await uploadBytes(ref(getStorage(), `${PATH}/uploadBytesBadMeta.json`), new ArrayBuffer(), 123);
1098+
await uploadBytes(
1099+
ref(getStorage(), `${PATH}/uploadBytesBadMeta.json`),
1100+
new ArrayBuffer(),
1101+
123,
1102+
);
11041103
return Promise.reject(new Error('Did not error!'));
11051104
} catch (error) {
11061105
error.message.should.containEql('must be an object value');

packages/storage/lib/StorageReference.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,25 @@ import type {
4747
} from './types/storage';
4848
import type { ListResultInternal, StorageInternal } from './types/internal';
4949

50+
async function putPayloadToUint8Array(data: Blob | Uint8Array | ArrayBuffer): Promise<Uint8Array> {
51+
if (data instanceof Uint8Array) {
52+
return data.byteOffset === 0 && data.byteLength === data.buffer.byteLength
53+
? data
54+
: new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
55+
}
56+
if (data instanceof ArrayBuffer) {
57+
return new Uint8Array(data);
58+
}
59+
return new Uint8Array(
60+
await new Promise<ArrayBuffer>((resolve, reject) => {
61+
const reader = new FileReader();
62+
reader.onload = () => resolve(reader.result as ArrayBuffer);
63+
reader.onerror = () => reject(reader.error);
64+
reader.readAsArrayBuffer(data);
65+
}),
66+
);
67+
}
68+
5069
export default class Reference extends ReferenceBase implements StorageReference {
5170
_storage: StorageInternal;
5271

@@ -206,6 +225,21 @@ export default class Reference extends ReferenceBase implements StorageReference
206225
put(data: Blob | Uint8Array | ArrayBuffer, metadata?: UploadMetadata): Task {
207226
const validatedMetadata = isUndefined(metadata) ? metadata : validateMetadata(metadata, false);
208227

228+
// Firebase-js fallback (e.g. macOS) exposes `uploadBinary`; iOS/Android native omit it.
229+
const uploadBinary = this._storage.native.uploadBinary;
230+
if (uploadBinary) {
231+
return new StorageUploadTask(this, async task => {
232+
const bytes = await putPayloadToUint8Array(data);
233+
return uploadBinary.call(
234+
this._storage.native,
235+
this.toString(),
236+
bytes,
237+
validatedMetadata,
238+
task._id,
239+
);
240+
});
241+
}
242+
209243
return new StorageUploadTask(this, task =>
210244
Base64.fromData(data).then(({ string, format }) => {
211245
const { _string, _format, _metadata } = this._updateString(

packages/storage/lib/types/internal.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,22 @@ export interface RNFBStorageModule {
158158
metadata: UploadMetadata | undefined,
159159
taskId: number,
160160
): Promise<TaskSnapshot>;
161+
162+
/** Optional on firebase-js fallback only; native modules use base64 + {@link putString}. */
163+
uploadBinary?(
164+
url: string,
165+
data: Uint8Array,
166+
metadata: UploadMetadata | undefined,
167+
taskId: number,
168+
): Promise<TaskSnapshot>;
169+
161170
putFile(
162171
url: string,
163172
filePath: string,
164173
metadata: UploadMetadata | undefined,
165174
taskId: number,
166175
): Promise<TaskSnapshot>;
176+
167177
writeToFile(url: string, filePath: string, taskId: number): Promise<TaskSnapshot>;
168178

169179
/**

0 commit comments

Comments
 (0)