How to read binary data response from AWS when doing a GET directly to an S3 URI in browser?

Some general context: This is an app that uses the MERN stack, but the question is more specific to AWS S3 data.

I have an S3 set up and i store images and files from my app there. I usually generate signedURLs with the server and do a direct upload from the browser.

within my app db i store the object URIs as a string and then an image for example i can render with an <img/> tag no problem. So far so good.

However, when they are PDFs and i want to let the user download the PDF i stored in S3, doing an <a href={s3Uri} download> just causes the pdf to be opened in another window/tab instead of prompting the user to download. I believe this is due to the download attribute being dependent on same-origin and you cannot download a file from an external resource (correct me if im wrong please)

So then my next attempt is to then do an http fetch of the resource directly using axios, it looks something like this

axios.create({
                baseURL: attachment.fileUrl,
                headers: {common: {Authorization: ''}}
            })
  .get('')
   .then(res => {
        console.log(res)
        console.log(typeof res.data)
        console.log(new Buffer.from(res.data).toString())
    })

So by doing this I am successfully reading the response headers (useful cuz then i can handle images/files differently) BUT when i try to read the binary data returned i have been unsuccessful and parsing it or even determining how it is encoded, it looks like this

%PDF-1.3
3 0 obj
<</Type /Page
/Parent 1 0 R
/Resources 2 0 R
/Contents 4 0 R>>
endobj
4 0 obj
<</Filter /FlateDecode /Length 1811>>
stream
x�X�R�=k=E׷�������Na˅��/���� �[�]��.�,��^ �wF0�.��Ie�0�o��ݧO_IoG����p��4�BJI���g��d|��H�$�12(R*oB��:%먺�����:�R�Ф6�Xɔ�[:�[��h�(�MQ���>���;l[[��VN�hK/][�!�mJC

.... and so on

I have another function I use to allow users to download PDFs that i store directly in my database as strings in base64. These are PDF’s my app generates and are fairly small so i store them directly in the DB, as opposed to the ones i store in AWS S3 which are user-submitted and can be several MBs in size (the ones in my db are just a few KB)

The function I use to process my base64 pdfs and provide a downloadable link to the users looks like this

export const makePdfUrlFromBase64 = (base64) => {
    const binaryImg = atob(base64);
    const binaryImgLength = binaryImg.length;
    const arrayBuffer = new ArrayBuffer(binaryImgLength);
    const uInt8Array = new Uint8Array(arrayBuffer);

    for (let i = 0;  i < binaryImgLength;  i++) {
        uInt8Array[i] = binaryImg.charCodeAt(i);
    }
    const outputBlob = new Blob([uInt8Array], {type: 'application/pdf'});
    return URL.createObjectURL(outputBlob)
}

HOWEVER, when i try to apply this function to the data returned from AWS i get this error:

DOMException: Failed to execute 'atob' on 'Window': The string to be decoded contains characters outside of the Latin1 range.

So what kind of binary data encoding do i have here from AWS?

Note: I am able to render an image with this binary data by passing the src in the img tag like this: <img src={data:${res.headers[‘Content-Type’]};base64,${res.data}} />

which is my biggest hint that this is some form of base64?

PLEASE! If anyone has a clue how i can achieve my goal here, im all ears! The goal is to be able to prompt the user to download the resource which i have in an S3 URI. I can link to it and they can open it in browser, and then download manually, but i want to force the prompt.

Anybody know what kind of data is being returned here? any way to parse it as a stream? a buffer?

I have tried to stringify it with JSON or to log it to the console as a string, im open to all suggestions at this point

Answer

You’re doing all kinds of unneeded conversions. When you do the GET request, you already have the data in the desired format.

const response = await fetch(attachment.fileUrl,
    headers: {Authorization: ''}}
});
const blob = await response.blob();
return URL.createObjectURL(res.data);