Skip to content

IMAGES-1608: Allow base64-encoded input and output in Images Binding #4141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ns476
Copy link
Contributor

@ns476 ns476 commented May 13, 2025

No description provided.

@ns476 ns476 requested review from a team as code owners May 13, 2025 14:27
@@ -12,7 +12,26 @@ type ImageInfoResponse =
};

type ImageTransform = {
width?: number;
Copy link
Contributor Author

@ns476 ns476 May 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had updated types/defines/images.d.ts with more accurate types, but not this file, hence the changes here - the diff on types/defines/images.d.ts is much more reasonable

* @param {Env} env
*/
async test(_, env) {
const inputs = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if there is a nice way to unit test the encode/decode transform streams

@@ -303,6 +327,165 @@ function chainStreams<T>(streams: ReadableStream<T>[]): ReadableStream<T> {
return outputStream;
}

function concatUint8Arrays(a: Uint8Array, b: Uint8Array): Uint8Array {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function doesn't take care of different offsets. Also we can use resizable array buffers, as far as I know, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/resize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

let base64Buffer: Uint8Array | null = null;
const asciiDecoder = new TextDecoder('ascii');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can create this in the global scope. No need to create this everytime this method is executed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While true, it's generally discouraged to create shared TextDecoder instances because they can contain state depending on what encoding is used and how they are utilized. Keeping this here makes sense I think.

try {
const base64SegmentString = asciiDecoder.decode(base64SegmentBytes);

const binaryString = atob(base64SegmentString);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atob is not recommended to be used in production environments. this will throw for utf8 values.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably either need to enable nodejs_compat for this case or expose similar APIs via a cloudflare-internal module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess as it is base64 we won't see any utf8 values if the base64 is valid, and it throws for invalid base64 anyway? Regardless using the Node APIs sounds way better if doable

@anonrig anonrig requested review from jasnell and npaun May 13, 2025 21:21
const binaryString = String.fromCharCode.apply(null, Array.from(buf));

const base64String = btoa(binaryString);
const base64Bytes = asciiEncoder.encode(base64String);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... we should probably look at providing a cloudflare-internal:... API to do the base64 encoding more efficiently. If nodejs_compat is on then I we could just use Buffers built-in base64 support. We could expose the same thing via a cloudflare-internal for times when nodejs_compat is not enabled.

const result = new Uint8Array(a.length + b.length);
result.set(a, 0);
result.set(b, a.length);
return result;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to become quite expensive as the buffers get bigger. If nodejs_compat was enabled then we could use Buffer.concat to be far more efficient.

@ns476 ns476 force-pushed the nskehin/base64-handling branch from 80975ef to dd356b8 Compare May 16, 2025 15:07
@ns476 ns476 force-pushed the nskehin/base64-handling branch from dd356b8 to e5d6c6e Compare May 16, 2025 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants