Displaying an image from S3

Hi,

I'm new to retool and struggling with a seemingly simple task: displaying an image (JPG/PNG) fetched from S3.

Using the "Read a file from S3" action in my S3 resource, I can get the image file. However, the data is marked as binary (Content-Type is "binary/octet-stream") but it looks like it has already been converted into a string ("data.Body" is a string).

How do I decode that string back into a binary? I suppose I would have to know how it was encoded in the first place. My idea was to convert the image to base64 and then feed it to the built-in Image component for display.

Thanks!

I just wanted to add that selecting "Download file from S3" returns what I'm looking for (the image, encoded in base64). However, this is not in the "data" property accessible from Retool. Screenshot from Chrome dev tools:

Hey @ftruzzi and welcome to the community! This is weird, I'm not able to repro locally. Reading an image file from my S3 bucket correctly returns Base64, and I'm able to render it in an image component. I think this might be an issue with how your bucket is configured.

1 Like

Hi @justin and thank you very much for taking the time to test this.

I noticed a different ContentType in your screenshot and I investigated a little bit more. This turned out to be a boto3 issue (the library I’m using to upload files) in which the content type defaults to binary/octet-stream: https://github.com/boto/boto3/issues/548

Manually specifying the content type as described in the above issue fixed it. Not sure why the “Download file from S3” action works anyway though, you might want to take a look at that.

Thanks!

1 Like

@justin unfortunately this issue also happens when uploading a file to S3 using the “Upload data” action from the s3 resource (Only binary content type is selectable)

From further research, this should not be an issue as calling btoa() on the data should return the correct base64-encoded image or file. However, btoa() crashes when being called on the data.Body object returned by the “Read file from S3” action (Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.)

Workflow:

  • Upload a b64 encoded image/file using the “Upload data” action type, “Binary” file type (atob is called in the background by the uploader)
  • Verify that the image is correctly uploaded on S3
  • Read the image using the “Read file from S3” action, you get a “binary string” in data.Body as per the screenshot in my first post
  • Create a transformer for the above action: return btoa(data.Body)

Expected result: You get your original file back
Actual result: Crashes with Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.

In other words: btoa(atob(str)) should be equal to str even if passing through S3. I’m not sure if I’m missing something here, as I’m pretty new to all this, but I think that should hold true.

Thanks!

Thanks for the detailed message! Definitely looks like a bug on our end - I’ll share with the engineering team and circle back when we have a fix (hopefully soon but can’t promise anything)!

What’s your preferred outcome here? Getting a non-binary string back from S3 when reading?

1 Like

I think it’s fine to return the raw data as a string (if it’s not possible to work with e.g a Blob object) but maybe it should be encoded differently. I did some more research and it looks like the issue is that the encoding of the data.Body string (UTF-8?) is not compatible with btoa() so images cannot be converted back to base64.

See https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary

I tried to convert the string using TextEncoder and a different base64 library which supports UTF-8 but it didn’t work (I got a different base64 out).

This led me to believe that maybe encoding is silently failing because of this?

When decoding a Buffer into a string that does not exclusively contain valid UTF-8 data, the Unicode replacement character U+FFFD ďż˝ will be used to represent those errors.

I’m not sure what the best solution is here, but encoding data.Body using the “binary” encoding (or, why not, “base64” directly) should make it work

If you need some data to reproduce the issue, this is the base64 .png image I’m trying with:

iVBORw0KGgoAAAANSUhEUgAAAFkAAABKCAYAAADQSfiaAAAC10lEQVR4Ae3BTW5bZRiG4ft+OTugIMqAFTBjyObYBbCeImWM1BUwYVAhQEI0VVs/nGMnadra+Wn82m75rsvvv/s2nIjffv+TUtYUmSnyhsoVRTbCLOFSEhYrIKvXJOHXp085hmJoVwztiqFdMbQrhnbF0K4Y2hVDu2JoVwztiqFdMbQrhnbF0K4Y2hVDu2JoVwztiqFdMbQrhnbF0K4Y2hVDu2JoVwztiqFdMbQrhnbF0K4Y2hVDu2JoVwztiqFdMbQrhnbF0K4Y2hVDu2JoVwztiqFdMbQrhnbF0K4Y2hWfCDldxdCu+MTJ8RVDu2JoV/wvyDEVnxI5ScUJkYeSU1R8xOTjUJwyQR5OmcmxFCdN7kNOU3EiknAruZ28RTYUXpyfcwzFiTh/8Yp7kR3kfQJydnbGMRQnIAl///OctyhyE9lGdvv5x584Pz/n0D775usvf+BIkvD8xUv++OtfXr56jYDKmiLvEETWFNlB2ebZs2f88uQJnz96xOPHj5mmiUPwq0dfhEORmVBSCEoBKigCKghYFO9QZEPlJiFkFRZJSEIICWS1IkAIWYWNQGgxoRyEgCKyJhQXZEPZELmB3IFAWKiEYAQCVZAVRlISZglIi0mayYYCslApNlRABGSmCMj75JLcRiAKCQuREFQggCwEwkwJ1wXCXkyW7J9ckQsiG7KhckVBWZPdBLkblTBLUAmzBBGVCCEYZkGuE2QtPMyE0kXkLYKIXKMgqCxkIbvJXclGFBJKiZLVioWACEKQXeRhprI4BNlCQCllIbcQ5H5USEAJwYBVhEA4iIkmcgMBRUVErlFkCwXkgygkiERmwQiyFkAW4UrYm6mUgysBEZC7UZEPIzMlCbIQhDBLkEtyRfZm4hBkJshMBGQLRbYQ5OFUwixhITNlEa4LhL2ZUPZGthB5Q3ZQZAtBi32RmbIIFxLkOkH2ZlLpIPegyBaCiPSQC0qn/wCKAqFhh2U5LQAAAABJRU5ErkJggg==

Thanks!

@ftruzzi full circle, I'm building an internal app right now and dealing with this problem myself :man_facepalming:

I've been able to get it to work by just using the Body value directly as Base64 without converting it. Either way, I wrote a bigger guide with a couple of other methods here: How do I display images from an S3 bucket in Retool?