How to compress image before uploading to Google Cloud Storage/S3?

I want to have my image compressed before they are uploaded to Google Cloud Storage or S3. How can I do that?

You could perhaps use something like the following in a "Run JS Code" query to rescale the image before uploading it:

function getImage(dataUrl)
{
return new Promise((resolve, reject) => {
const image = new Image();
image.src = dataUrl;
image.onload = () => {
resolve(image);
};
image.onerror = (el, err) => {
reject(err.error);
};
});
}

async function downscaleImage(
dataUrl,
imageType, // e.g. 'image/jpeg'
resolution, // max width/height in pixels
quality // e.g. 0.9 = 90% quality
) {

// Create a temporary image so that we can compute the height of the image.
const image = await getImage(dataUrl);
const oldWidth = image.naturalWidth;
const oldHeight = image.naturalHeight;
console.log('dims', oldWidth, oldHeight);

const longestDimension = oldWidth > oldHeight ? 'width' : 'height';
const currentRes = longestDimension == 'width' ? oldWidth : oldHeight;
console.log('longest dim', longestDimension, currentRes);

if (currentRes > resolution) {
console.log('need to resize...');

// Calculate new dimensions
const newSize = longestDimension == 'width' ? Math.floor(oldHeight / oldWidth * resolution) : Math.floor(oldWidth / oldHeight * resolution);
const newWidth = longestDimension == 'width' ? resolution : newSize;
const newHeight = longestDimension == 'height' ? resolution : newSize;
console.log('new width / height', newWidth, newHeight);

// Create a temporary canvas to draw the downscaled image on.
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;

// Draw the downscaled image on the canvas and return the new data URL.
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, newWidth, newHeight);
const newDataUrl = canvas.toDataURL(imageType, quality);
return newDataUrl;
}
else {
return dataUrl;
}

}

let dataUrl = 'data:'+fileButton1.files[0].type+';base64,'+fileButton1.value[0];
let imageType = fileButton1.files[0].type
let resolution = 200
let quality = .8


let ds = downscaleImage(
dataUrl,
imageType, // e.g. 'image/jpeg'
resolution, // max width/height in pixels
quality // e.g. 0.9 = 90% quality
)

return ds

I also found the following library that seems to work in Retool, which could work for you as well: https://cdnjs.cloudflare.com/ajax/libs/jic/2.0.2/JIC.min.js

2 Likes

How to use this ds data to rest form/data upload api?

Hey @rony! If you're returning ds from a JS query, you can access the return from the query like {{query_name.data}} pretty much anywhere in Retool, including in a REST query.

How you set up that REST query depends on your endpoint setup and the action you're trying to perform.

Let me know if I can help answer any other questions :blush:

@victoria @mark How would I modify this to output the result in binary?

1 Like

Hey @nroeder! What format are you starting with?

Two scenarios, using the camera component in the mobile app, which gives a png, second is a file input which could be png, jpg, etc.

I can upload directly to azure using the {{fileInput1.value['0']}} body binary.

however if I use the compression JavaScript above I get a base64 output and when I upload the file and try to open I get a cannot ready file.

This is the output from the compression

"data:image/jpeg;base64,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"

Ah, got it! It looks like it's returning some additional info and the base64, so we can do something like this to just return the base64 (you can put the code directly in your image source field)

your_output_string.split("base64,")[1]

Let me know if this works for you!

it works perfectly for jpeg but when I am using png instead of decreasing the size its actually increases the size

Hi,

I cannot get this code to work. The js is still 'thinking' five minutes after providing a jpg via the fileButton. I have set the parameters as follows:

async function downscaleImage(
dataUrl,
imageType = "image/jpeg", // e.g. 'image/jpeg'
resolution = 600/800, // max width/height in pixels
quality = 0.4 // e.g. 0.9 = 90% quality
)

let ds = downscaleImage(
dataUrl,
imageType = "image/jpeg", // e.g. 'image/jpeg'
resolution = 600/800, // max width/height in pixels
quality = 0.4 // e.g. 0.9 = 90% quality
)

It doesn't throw an error, the little blue wheel just keeps a-turnin'.

Hi @patient_but_frustrated, are you still working on this? I noticed the original solution is from a few years back so it's possible things have been updated since then. Would you mind walking me through the problem and what you're goal end result is?

Hi I'm a different person but I'm trying to compress/resize images in Retool and am struggling with the above code as well. The code above just pinwheels for me.

I have imported a library that compresses photos but it requires a File or Blob object which I can't seem to get from the upload component. Can you help with this?

Hi @eslinz,

Happy to help. What library are you using to compress images?