Skip to content

Commit

Permalink
remove FileReader where possible
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Oct 17, 2024
1 parent ca2f75c commit 3800626
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 62 deletions.
7 changes: 7 additions & 0 deletions .changeset/breezy-cobras-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@smithy/chunked-blob-reader": major
"@smithy/fetch-http-handler": major
"@smithy/chunked-blob-reader-native": patch
---

replace FileReader with Blob.arrayBuffer() where possible
5 changes: 0 additions & 5 deletions .changeset/fluffy-planes-grow.md

This file was deleted.

4 changes: 4 additions & 0 deletions packages/chunked-blob-reader-native/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export function blobReader(
chunkSize: number = 1024 * 1024
): Promise<void> {
return new Promise((resolve, reject) => {
/**
* TODO(react-native): https://github.com/facebook/react-native/issues/34402
* To drop FileReader in react-native, we need the Blob.arrayBuffer() method to work.
*/
const fileReader = new FileReader();

fileReader.onerror = reject;
Expand Down
5 changes: 5 additions & 0 deletions packages/chunked-blob-reader/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Blob as BlobPolyfill } from "buffer";

import { blobReader } from "./index";

// jsdom inaccurate Blob https://github.com/jsdom/jsdom/issues/2555.
global.Blob = BlobPolyfill as any;

describe("blobReader", () => {
it("reads an entire blob", async () => {
const longMessage: number[] = [];
Expand Down
37 changes: 9 additions & 28 deletions packages/chunked-blob-reader/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
/**
* @internal
* Reads the blob data into the onChunk consumer.
*/
export function blobReader(
export async function blobReader(
blob: Blob,
onChunk: (chunk: Uint8Array) => void,
chunkSize: number = 1024 * 1024
): Promise<void> {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
const size = blob.size;
let totalBytesRead = 0;

fileReader.addEventListener("error", reject);
fileReader.addEventListener("abort", reject);

const size = blob.size;
let totalBytesRead = 0;

function read() {
if (totalBytesRead >= size) {
resolve();
return;
}
fileReader.readAsArrayBuffer(blob.slice(totalBytesRead, Math.min(size, totalBytesRead + chunkSize)));
}

fileReader.addEventListener("load", (event) => {
const result = <ArrayBuffer>(event.target as any).result;
onChunk(new Uint8Array(result));
totalBytesRead += result.byteLength;
// read the next block
read();
});

// kick off the read
read();
});
while (totalBytesRead < size) {
const slice: Blob = blob.slice(totalBytesRead, Math.min(size, totalBytesRead + chunkSize));
onChunk(new Uint8Array(await slice.arrayBuffer()));
totalBytesRead += slice.size;
}
}
49 changes: 21 additions & 28 deletions packages/fetch-http-handler/src/stream-collector.spec.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,29 @@
import { Blob as BlobPolyfill } from "buffer";

import { streamCollector } from "./stream-collector";

/**
* Have to mock the FileReader behavior in IE, where
* reader.result is null if reads an empty blob.
*/
// jsdom inaccurate Blob https://github.com/jsdom/jsdom/issues/2555.
global.Blob = BlobPolyfill as any;

describe("streamCollector", () => {
let originalFileReader = (global as any).FileReader;
let originalBlob = (global as any).Blob;
beforeAll(() => {
originalFileReader = (global as any).FileReader;
originalBlob = (global as any).Blob;
});
afterAll(() => {
(global as any).FileReader = originalFileReader;
(global as any).Blob = originalBlob;
const blobAvailable = typeof Blob === "function";
const readableStreamAvailable = typeof ReadableStream === "function";

(blobAvailable ? it : it.skip)("collects Blob into bytearray", async () => {
const blobby = new Blob([new Uint8Array([1, 2]), new Uint8Array([3, 4])]);
const collected = await streamCollector(blobby);
expect(collected).toEqual(new Uint8Array([1, 2, 3, 4]));
});

it("returns a Uint8Array when blob is empty and when FileReader data is null(in IE)", (done) => {
(global as any).FileReader = function FileReader() {
this.result = null; //In IE, FileReader.result is null after reading empty blob
this.readAsDataURL = jest.fn().mockImplementation(() => {
if (this.onloadend) {
this.readyState = 2;
this.onloadend();
}
});
};
(global as any).Blob = function Blob() {};
const dataPromise = streamCollector(new Blob());
dataPromise.then((data: any) => {
expect(data).toEqual(Uint8Array.from([]));
done();
(readableStreamAvailable ? it : it.skip)("collects ReadableStream into bytearray", async () => {
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([1, 2]));
controller.enqueue(new Uint8Array([3, 4]));
controller.close();
},
});
const collected = await streamCollector(stream);
expect(collected).toEqual(new Uint8Array([1, 2, 3, 4]));
});
});
1 change: 0 additions & 1 deletion packages/fetch-http-handler/src/stream-collector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { StreamCollector } from "@smithy/types";

//reference: https://snack.expo.io/r1JCSWRGU
export const streamCollector: StreamCollector = async (stream: Blob | ReadableStream): Promise<Uint8Array> => {
if (typeof Blob === "function" && stream instanceof Blob) {
return new Uint8Array(await stream.arrayBuffer());
Expand Down

0 comments on commit 3800626

Please sign in to comment.