Convert Request Body to base64 encoded string [Solved]

Hi

I have an interesting situation trying to create a “proxy” of sorts to translate the request body between a client and origin (raw request body to a base64 encoded string)

The following snippet works when testing in the console and the incoming request body is pure text

let newBody = await request.text()
console.log(newBody)    // returns text input
console.log(btoa(newBody)) // returns the base64 representation of the text input

But because my real world request is simply byte data, I believe I need to do the following,

let newBody = await request.body
console.log(newBody)    // returns ReadableString {}
console.log(btoa(newBody)) // returns base64 representation of the text "[object ReadableStream]"

Edit: Also tried using request.blob() receiving the error “Error: Failed to execute ‘blob’ on ‘Body’: the method is not implemented.”

Can you advise how to access the “readable stream” to actually extract the data for conversion to base64? My impression was the use of await forced the worker to wait until the request body was finished being received by the worker.

I’m an IT Pro so may have missed a very obvious dev related step
Russell

From a quick glance you just need to not await request.body (properties can’t be awaited) https://developer.mozilla.org/en-US/docs/Web/API/Body/body

You can do something like this instead.

  let string = '';
  (new Uint8Array(await request.arrayBuffer())).forEach(
      (byte) => { string += String.fromCharCode(byte) }
    )
  string = btoa(string);
1 Like

Big thanks @hero! your sample led me to this -> https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String which is now working perfectly with btoa and a Uint8Array

I’m also getting the response back now from the origin and with that same method (String.fromCharcode…) converting the response bytes to the returned base64 string succesfully.

The worker is happily returning the base64 string through a custom response, I now just need to convert it back to its original format. Unfortunately the str2ab function in the above link is not working. I’m starting my investigations into this now, but if you have any tips, they would be greatly appreaciated!

Thanks again
Russell

Hi @Judge, in all my testing, without the await, I only ever got promises with no data.

The str2ab function in your sample is essentially a simple text encoder; it writes a string to a buffer, 2 bytes for each character. When you pass a base 64–encoded text to atob, you get a string where each character represents a byte of the original data. To convert this string to a byte buffer, what you need to do is copy its characters “verbatim” (as single bytes).

function str2ab (string) {
  const
    length = string.length,
    buf = new ArrayBuffer(length),
    bufView = new Uint8Array(buf);
  for (var i = 0; i < length; i++) { bufView[i] = string.charCodeAt(i) }
  return buf
}

Here’s a full worker script showing the whole process, which you may find helpful.

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

const imageUrl =
  'https://upload.wikimedia.org/wikipedia/commons/thumb/7/72/' +
  'Cat_playing_with_a_lizard.jpg/1200px-Cat_playing_with_a_lizard.jpg';

function base64Encode (buf) {
  let string = '';
  (new Uint8Array(buf)).forEach(
      (byte) => { string += String.fromCharCode(byte) }
    )
  return btoa(string)
}

function base64Decode (string) {
  string = atob(string);
  const
    length = string.length,
    buf = new ArrayBuffer(length),
    bufView = new Uint8Array(buf);
  for (var i = 0; i < length; i++) { bufView[i] = string.charCodeAt(i) }
  return buf
}

async function handleRequest(request) {
  const
    upstreamResponse = await fetch(imageUrl),
    text = base64Encode(await upstreamResponse.arrayBuffer()),
    bin = base64Decode(text);
  
  return new Response(bin, {status: 200, headers: {
      'Content-type': upstreamResponse.headers.get('Content-type')
    }})
}

Edit: For more about ArrayBuffer, see this article.

1 Like

I think people would love this in the recipe exchange, so if you have the time to do a write-up about ArrayBuffers, their assigned types and explain how it works, I think it will help a lot of users!

You would be a @hero :wink:

1 Like

Aww. I guess @RussT can simply add a “recipe-exchange” tag to this thread. There’s already good documentation for ArrayBuffer which I’ll add to my reply above.

recipe-exchange tag added to help location.

Thanks again @hero , after a few more stumbles to remove some extra double quotes that were getting injected somewhere, the entire worker is now working like a dream !

1 Like