Skip to content

Instantly share code, notes, and snippets.

@ORESoftware
Last active October 13, 2024 17:55
Show Gist options
  • Save ORESoftware/ba5d03f3e1826dc15d5ad2bcec37f7bf to your computer and use it in GitHub Desktop.
Save ORESoftware/ba5d03f3e1826dc15d5ad2bcec37f7bf to your computer and use it in GitHub Desktop.
resizing an image on the front-end before sending to a server
// Using this code, we can retrieve an image from a user's filesystem, resize the image, and then upload the image
// to a server using AJAX. Because we use base64 encoding, we can just include the image data as just another string value
// in a JSON payload.
// So we can use AJAX to send the file to a server, which is convenient.
// We have one line of relevant html
// get file in the first place => <input type="file" custom-on-change="onAcqImageFileChange" class="form-control">
$scope.onAcqImageFileChange = function (e) {
e.preventDefault();
var file = e.target.files[0];
$scope.acqImageFile = file; // store reference to file
};
function convertToBase64(file, cb) {
var reader = new FileReader();
reader.onload = function (e) {
cb(null, e.target.result)
};
reader.onerror = function (e) {
cb(e);
};
reader.readAsDataURL(file);
}
function resizeImage(base64Str) {
var img = new Image();
img.src = base64Str;
var canvas = document.createElement('canvas');
var MAX_WIDTH = 400;
var MAX_HEIGHT = 350;
var width = img.width;
var height = img.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
return canvas.toDataURL();
}
$scope.submitImageSelection = function () {
// when the user finally selects the image they want, and clicks submit, we run this fn
var imgFile = $scope.acqImageFile;
convertToBase64(imgFile, function (err, data) {
if (err) {
/// handle error
return;
}
// resize the image like a boss
data = resizeImage(data);
// finally we can send the data to a server as
// just another field in a JSON payload
SomeService.sendDataToServer(data, function(err){
// voila
});
});
};
// note that to display the image selection back to the user before they choose their final selection, you may want to convert to
// base64 each time they change selections.
// to display the image, you can use ng-src:
// <img class="overlay-image" ng-src="{{acqImageData ? acqImageData : '/assets/img/placeholder.png'}}">
// <the end>
@sayamkeshri
Copy link

great

@odyssic
Copy link

odyssic commented Oct 8, 2021

Except this doesn't make the file size smaller, correct? Because it is still a string regardless of the image dimensions? I'm working with base64 but the page loads so slowly and I see a loooong string loading.

@gs-nasc
Copy link

gs-nasc commented Oct 8, 2021

@odyssic No, this doesn't make yout file size smaller (it should even decrease, but very little) so that i recommend is compress your file with any compressor that you find in the internet (like this https://compressjpeg.com/) or make your compressor (like this https://stackoverflow.com/questions/14672746/how-to-compress-an-image-via-javascript-in-the-browser)

@sadikyalcin
Copy link

Does this preserve image quality? Reducing dimensions of a canvas destroys the image quality when you're scale it down.

@gs-nasc
Copy link

gs-nasc commented Oct 29, 2021

@sadikyalcin it depends on how much you're going to compress, in my tests the quality hasn't changed that much
5425393SZ
image
image

Here are the image tests I did. Logically, the more I change its scaling, the more quality is lost, but the loss is not so much.

@sadikyalcin
Copy link

sadikyalcin commented Oct 29, 2021

@gs-nasc That is acceptable. I've done a few myself and got slightly better results by passing the native type and 1 to toDataUrl method (canvas.toDataURL('image/png', 1).

@greentoss
Copy link

and how to make unit tests for this function ?

Hello, it wasn't work for me, so i change somethings:

function resizeImage:

function resizeImage(base64Str, maxWidth = 400, maxHeight = 350) {
  return new Promise((resolve) => {
    let img = new Image()
    img.src = base64Str
    img.onload = () => {
      let canvas = document.createElement('canvas')
      const MAX_WIDTH = maxWidth
      const MAX_HEIGHT = maxHeight
      let width = img.width
      let height = img.height

      if (width > height) {
        if (width > MAX_WIDTH) {
          height *= MAX_WIDTH / width
          width = MAX_WIDTH
        }
      } else {
        if (height > MAX_HEIGHT) {
          width *= MAX_HEIGHT / height
          height = MAX_HEIGHT
        }
      }
      canvas.width = width
      canvas.height = height
      let ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0, width, height)
      resolve(canvas.toDataURL())
    }
  })
}

Usage:

resizeImage(e.target.result, 1000, 1000).then((result) => {
    console.log(result);
});

Thanks for the code

@servusdei2018
Copy link

Except this doesn't make the file size smaller, correct? Because it is still a string regardless of the image dimensions? I'm working with base64 but the page loads so slowly and I see a loooong string loading.

It does make the file size smaller because a smaller dimensioned image means less pixels. If you want to further reduce file size, use canvas.toDataURL('image/jpeg', 0.8) instead of just canvas.toDataURL(), since by default it saves it as a PNG with max quality. Using JPEG format at 80% quality is hardly noticeable in terms of image appearance, but the image size is much much smaller.

@stephanbogner
Copy link

This is awesome, thanks!

@LeandroTRibeiro
Copy link

amazing, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment