cast/convert ReadableStream<Uint8Array> to Uint8ClampedArray

76 Views Asked by At

I have a ReadableStream<Uint8Array> and need to convert it to Uint8ClampedArray in TypeScript Deno.

I tried some solutions I could find. Like this:

const getImageData = image => {
  const canvas = document.createElement("canvas");
  canvas.width = image.width;
  canvas.height = image.height;
  const context = canvas.getContext("2d");
  if (context != null) {
    context.drawImage(image, 0, 0);
    return context.getImageData(0, 0, image.width, image.height);
  }
};

But it already fails because document can't be found.

Is there a straight forward casting/converting from ReadableStream<Uint8Array> to Uint8ClampedArray?

My ReadableStream comes from this:

const image = (await fetch(url)).body!;

I want to do all this, to create a blurhash. But when just using the Uint8Array I get ValidationError: Width and height must match the pixels array

1

There are 1 best solutions below

8
On BEST ANSWER

If you have a ReadableStream and don't know the byte length of the stream, then you can collect the bytes and concatenate them in order to construct an instance of Uint8ClampedArray:

Code in TypeScript Playground

/** Consumes the stream */
async function intoUint8ClampedArray(
  readable: ReadableStream<Uint8Array>,
): Promise<Uint8ClampedArray> {
  const chunks: Uint8Array[] = [];
  let cursor = 0;

  await readable.pipeTo(
    new WritableStream({
      write(chunk) {
        cursor += chunk.byteLength;
        chunks.push(chunk);
      },
    }),
  );

  const clamped = new Uint8ClampedArray(cursor);
  cursor = 0;

  for (const chunk of chunks) {
    clamped.set(chunk, cursor);
    cursor += chunk.byteLength;
  }

  return clamped;
}

Use it with your example code like this:

const response = await fetch("https://example.com/");

if (!response.body) {
  // Handle case where body is null here…
  throw new Error("Body null");
}

const clamped = await intoUint8ClampedArray(response.body);

// Use the Uint8ClampedArray…

However, if you're starting with an instance of Response then you can use its async method arrayBuffer() to construct the clamped array more simply:

const response = await fetch("https://example.com/");
const clamped = new Uint8ClampedArray(await response.arrayBuffer());
// Use the Uint8ClampedArray…