Skip to content

Commit

Permalink
fix(fetch-http-handler): add polyfill to collect Blob in react-native (
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr authored Jan 2, 2025
1 parent b2d54aa commit 1dd6ace
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/metal-emus-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/fetch-http-handler": patch
---

Add polyfill to collect Blob in react-native environments
35 changes: 34 additions & 1 deletion packages/fetch-http-handler/src/stream-collector.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { StreamCollector } from "@smithy/types";
import { fromBase64 } from "@smithy/util-base64";

export const streamCollector: StreamCollector = async (stream: Blob | ReadableStream): Promise<Uint8Array> => {
if ((typeof Blob === "function" && stream instanceof Blob) || stream.constructor?.name === "Blob") {
return new Uint8Array(await (stream as Blob).arrayBuffer());
if (Blob.prototype.arrayBuffer !== undefined) {
return new Uint8Array(await (stream as Blob).arrayBuffer());
}
return collectBlob(stream as Blob);
}

return collectStream(stream as ReadableStream);
};

async function collectBlob(blob: Blob): Promise<Uint8Array> {
const base64 = await readToBase64(blob);
const arrayBuffer = fromBase64(base64);
return new Uint8Array(arrayBuffer);
}

async function collectStream(stream: ReadableStream): Promise<Uint8Array> {
const chunks = [];
const reader = stream.getReader();
Expand All @@ -32,3 +42,26 @@ async function collectStream(stream: ReadableStream): Promise<Uint8Array> {

return collected;
}

function readToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
// reference: https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
// response from readAsDataURL is always prepended with "data:*/*;base64,"
if (reader.readyState !== 2) {
return reject(new Error("Reader aborted too early"));
}
const result = (reader.result ?? "") as string;
// Response can include only 'data:' for empty blob, return empty string in this case.
// Otherwise, return the string after ','
const commaIndex = result.indexOf(",");
const dataOffset = commaIndex > -1 ? commaIndex + 1 : result.length;
resolve(result.substring(dataOffset));
};
reader.onabort = () => reject(new Error("Read aborted"));
reader.onerror = () => reject(reader.error);
// reader.readAsArrayBuffer is not always available
reader.readAsDataURL(blob);
});
}

0 comments on commit 1dd6ace

Please sign in to comment.