Skip to content

Instantly share code, notes, and snippets.

@dcollien
Last active April 28, 2023 09:00
Show Gist options
  • Save dcollien/312bce1270a5f511bf4a to your computer and use it in GitHub Desktop.
Save dcollien/312bce1270a5f511bf4a to your computer and use it in GitHub Desktop.
Resize Images in the Browser
let hasBlobConstructor = typeof(Blob) !== 'undefined' && (function () {
try {
return Boolean(new Blob());
} catch (e) {
return false;
}
}());
let hasArrayBufferViewSupport = hasBlobConstructor && typeof(Uint8Array) !== 'undefined' && (function () {
try {
return new Blob([new Uint8Array(100)]).size === 100;
} catch (e) {
return false;
}
}());
let hasToBlobSupport = (typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement.prototype.toBlob : false);
let hasBlobSupport = (hasToBlobSupport || (typeof Uint8Array !== 'undefined' && typeof ArrayBuffer !== 'undefined' && typeof atob !== 'undefined'));
let hasReaderSupport = (typeof FileReader !== 'undefined' || typeof URL !== 'undefined');
export default class ImageTools {
static resize(file, maxDimensions, callback) {
if (typeof maxDimensions === 'function') {
callback = maxDimensions;
maxDimensions = {
width: 640,
height: 480
};
}
let maxWidth = maxDimensions.width;
let maxHeight = maxDimensions.height;
if (!ImageTools.isSupported() || !file.type.match(/image.*/)) {
callback(file, false);
return false;
}
if (file.type.match(/image\/gif/)) {
// Not attempting, could be an animated gif
callback(file, false);
// TODO: use https://github.com/antimatter15/whammy to convert gif to webm
return false;
}
let image = document.createElement('img');
image.onload = (imgEvt) => {
let width = image.width;
let height = image.height;
let isTooLarge = false;
if (width >= height && width > maxDimensions.width) {
// width is the largest dimension, and it's too big.
height *= maxDimensions.width / width;
width = maxDimensions.width;
isTooLarge = true;
} else if (height > maxDimensions.height) {
// either width wasn't over-size or height is the largest dimension
// and the height is over-size
width *= maxDimensions.height / height;
height = maxDimensions.height;
isTooLarge = true;
}
if (!isTooLarge) {
// early exit; no need to resize
callback(file, false);
return;
}
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
if (hasToBlobSupport) {
canvas.toBlob((blob) => {
callback(blob, true);
}, file.type);
} else {
let blob = ImageTools._toBlob(canvas, file.type);
callback(blob, true);
}
};
ImageTools._loadImage(image, file);
return true;
}
static _toBlob(canvas, type) {
let dataURI = canvas.toDataURL(type);
let dataURIParts = dataURI.split(',');
let byteString;
if (dataURIParts[0].indexOf('base64') >= 0) {
// Convert base64 to raw binary data held in a string:
byteString = atob(dataURIParts[1]);
} else {
// Convert base64/URLEncoded data component to raw binary data:
byteString = decodeURIComponent(dataURIParts[1]);
}
let arrayBuffer = new ArrayBuffer(byteString.length);
let intArray = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
let mimeString = dataURIParts[0].split(':')[1].split(';')[0];
let blob = null;
if (hasBlobConstructor) {
blob = new Blob(
[hasArrayBufferViewSupport ? intArray : arrayBuffer],
{type: mimeString}
);
} else {
let bb = new BlobBuilder();
bb.append(arrayBuffer);
blob = bb.getBlob(mimeString);
}
return blob;
}
static _loadImage(image, file, callback) {
if (typeof(URL) === 'undefined') {
let reader = new FileReader();
reader.onload = function(evt) {
image.src = evt.target.result;
if (callback) { callback(); }
}
reader.readAsDataURL(file);
} else {
image.src = URL.createObjectURL(file);
if (callback) { callback(); }
}
};
static isSupported() {
return (
(typeof(HTMLCanvasElement) !== 'undefined')
&& hasBlobSupport
&& hasReaderSupport
);
}
}
'use strict';
if (typeof exports === "undefined") {
var exports = {};
}
if (typeof module === "undefined") {
var module = {};
}
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var hasBlobConstructor = typeof Blob !== 'undefined' && (function () {
try {
return Boolean(new Blob());
} catch (e) {
return false;
}
})();
var hasArrayBufferViewSupport = hasBlobConstructor && typeof Uint8Array !== 'undefined' && (function () {
try {
return new Blob([new Uint8Array(100)]).size === 100;
} catch (e) {
return false;
}
})();
var hasToBlobSupport = typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement.prototype.toBlob : false;
var hasBlobSupport = hasToBlobSupport || typeof Uint8Array !== 'undefined' && typeof ArrayBuffer !== 'undefined' && typeof atob !== 'undefined';
var hasReaderSupport = typeof FileReader !== 'undefined' || typeof URL !== 'undefined';
var ImageTools = (function () {
function ImageTools() {
_classCallCheck(this, ImageTools);
}
_createClass(ImageTools, null, [{
key: 'resize',
value: function resize(file, maxDimensions, callback) {
if (typeof maxDimensions === 'function') {
callback = maxDimensions;
maxDimensions = {
width: 640,
height: 480
};
}
var maxWidth = maxDimensions.width;
var maxHeight = maxDimensions.height;
if (!ImageTools.isSupported() || !file.type.match(/image.*/)) {
callback(file, false);
return false;
}
if (file.type.match(/image\/gif/)) {
// Not attempting, could be an animated gif
callback(file, false);
// TODO: use https://github.com/antimatter15/whammy to convert gif to webm
return false;
}
var image = document.createElement('img');
image.onload = function (imgEvt) {
var width = image.width;
var height = image.height;
var isTooLarge = false;
if (width > height && width > maxDimensions.width) {
// width is the largest dimension, and it's too big.
height *= maxDimensions.width / width;
width = maxDimensions.width;
isTooLarge = true;
} else if (height > maxDimensions.height) {
// either width wasn't over-size or height is the largest dimension
// and the height is over-size
width *= maxDimensions.height / height;
height = maxDimensions.height;
isTooLarge = true;
}
if (!isTooLarge) {
// early exit; no need to resize
callback(file, false);
return;
}
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
if (hasToBlobSupport) {
canvas.toBlob(function (blob) {
callback(blob, true);
}, file.type);
} else {
var blob = ImageTools._toBlob(canvas, file.type);
callback(blob, true);
}
};
ImageTools._loadImage(image, file);
return true;
}
}, {
key: '_toBlob',
value: function _toBlob(canvas, type) {
var dataURI = canvas.toDataURL(type);
var dataURIParts = dataURI.split(',');
var byteString = undefined;
if (dataURIParts[0].indexOf('base64') >= 0) {
// Convert base64 to raw binary data held in a string:
byteString = atob(dataURIParts[1]);
} else {
// Convert base64/URLEncoded data component to raw binary data:
byteString = decodeURIComponent(dataURIParts[1]);
}
var arrayBuffer = new ArrayBuffer(byteString.length);
var intArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
var mimeString = dataURIParts[0].split(':')[1].split(';')[0];
var blob = null;
if (hasBlobConstructor) {
blob = new Blob([hasArrayBufferViewSupport ? intArray : arrayBuffer], { type: mimeString });
} else {
var bb = new BlobBuilder();
bb.append(arrayBuffer);
blob = bb.getBlob(mimeString);
}
return blob;
}
}, {
key: '_loadImage',
value: function _loadImage(image, file, callback) {
if (typeof URL === 'undefined') {
var reader = new FileReader();
reader.onload = function (evt) {
image.src = evt.target.result;
if (callback) {
callback();
}
};
reader.readAsDataURL(file);
} else {
image.src = URL.createObjectURL(file);
if (callback) {
callback();
}
}
}
}, {
key: 'isSupported',
value: function isSupported() {
return typeof HTMLCanvasElement !== 'undefined' && hasBlobSupport && hasReaderSupport;
}
}]);
return ImageTools;
})();
exports['default'] = ImageTools;
module.exports = exports['default'];
@leventengin
Copy link

thanks for the script..
i plan to use it in direct uploads from mobile devices. but i get the results of all portrait images in landscape form (turned down) . is there a way to change it, i could not find a way in the code... my images are raw image files from mobile phones...landscapes are ok.
thanks again..........

@frutality
Copy link

@dcollien have you tried to use Hermite? I tried and see no difference in output. Files are same, byte-to-byte, both in FF and Chrome. It makes me think I'm doing something wrong.

P.S. You wrote "and replace the ctx.drawImage call with resample_single", but in that case we are not drawing image. Result is black screen. I added this after "ctx.drawImage":

HERMITE.resample_single(canvas, width, height, true);

And nothing changed. I'm using ES6 version.

@frutality
Copy link

My bad, it was hot reloading. It was not doing right. After manual page refresh everything works. Thanks.

@qomi210
Copy link

qomi210 commented May 5, 2018

I use this js in my site but in some pc its hang out when generating resized blob who know what I must do ?
reply to [email protected]

@MatthewLHolden
Copy link

Is there anyway to pass a base64 string into this?

ImageTools.resize(this.selectedFiles[0], { width: 1000, height: 1000 }, ((blob, didItResize) => {

I'm trying to resolve a scenario whereby an image taken from an iPhone has been transferred to desktop. When using that image to upload through my web app, the orientation is incorrect. I have some code that fixes the orientation, but by that point it's already a base64 string. It'd be really great to know whether I can resize that base64 variable using your script. It that possible at all?

@talhakhankhalil
Copy link

How should I send the resized blob to server in POST request.

@SagiMedina
Copy link

I'v wrote one with orientation fix using exif, @leventengin or anyone else how looks for a solution
https://gist.github.com/SagiMedina/f00a57de4e211456225d3114fd10b0d0

@TonyC5
Copy link

TonyC5 commented Aug 25, 2018

@SagiMedina - thanks for posting your solution. I'm new to js programming and trying to figure things out. I could be wrong (very easily), but it appears to me that your ImageTools.js is not a direct replacement for @dcollien's ImageTools.js. Am I missing something? Did you change the calling signature of ImageTools.resize()? If so, how do you use your solution? Thank you.

@naxo25
Copy link

naxo25 commented Aug 19, 2020

Thanks! Gracias por la solución!

@produktive
Copy link

Here is an updated gist that adds another parameter, avatar, which takes an integer. If supplied, it will scale the image to the avatar parameter, and center-crop the image into a square. Use as ImageTools.resize(event.target.files[0], 90, {...}

'use strict';

if (typeof exports === "undefined") {
    var exports = {};
}

if (typeof module === "undefined") {
   var module = {};
}

Object.defineProperty(exports, '__esModule', {
    value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var hasBlobConstructor = typeof Blob !== 'undefined' && (function () {
    try {
        return Boolean(new Blob());
    } catch (e) {
        return false;
    }
})();

var hasArrayBufferViewSupport = hasBlobConstructor && typeof Uint8Array !== 'undefined' && (function () {
    try {
        return new Blob([new Uint8Array(100)]).size === 100;
    } catch (e) {
        return false;
    }
})();

var hasToBlobSupport = typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement.prototype.toBlob : false;

var hasBlobSupport = hasToBlobSupport || typeof Uint8Array !== 'undefined' && typeof ArrayBuffer !== 'undefined' && typeof atob !== 'undefined';

var hasReaderSupport = typeof FileReader !== 'undefined' || typeof URL !== 'undefined';

var ImageTools = (function () {
    function ImageTools() {
        _classCallCheck(this, ImageTools);
    }

    _createClass(ImageTools, null, [{
        key: 'resize',
        value: function resize(file, avatar = false, maxDimensions, callback) {
            if (typeof maxDimensions === 'function') {
                callback = maxDimensions;
                maxDimensions = {
                    width: 640,
                    height: 480
                };
            }

            var maxWidth = maxDimensions.width;
            var maxHeight = maxDimensions.height;

            if (!ImageTools.isSupported() || !file.type.match(/image.*/)) {
                callback(file, false);
                return false;
            }

            if (file.type.match(/image\/gif/)) {
                // Not attempting, could be an animated gif
                callback(file, false);
                // TODO: use https://github.com/antimatter15/whammy to convert gif to webm
                return false;
            }

            var image = document.createElement('img');

            image.onload = function (imgEvt) {
                var width = image.width;
                var height = image.height;
                var isTooLarge = false;

				const getWidthHeight = function(img, side) {
				  const { width, height } = img;
				  if (width === height) {
				    return [0, 0, side, side];
				  } else if (width < height) {
				    const rat = height / width;
				    const top = ((side * rat) - side) / 2;
				    return [0, -1 * top, side, side * rat]
				  } else {
				    const rat = width / height;
				    const left = ((side * rat) - side) / 2;
				    return [-1 * left, 0, side * rat, side]
				  }
				}
				
				if (!avatar) {
					if (width > height && width > maxDimensions.width) {
	                    // width is the largest dimension, and it's too big.
	                    height *= maxDimensions.width / width;
	                    width = maxDimensions.width;
	                    isTooLarge = true;
	                } else if (height > maxDimensions.height) {
	                    // either width wasn't over-size or height is the largest dimension
	                    // and the height is over-size
	                    width *= maxDimensions.height / height;
	                    height = maxDimensions.height;
	                    isTooLarge = true;
	                }

	                if (!isTooLarge) {
	                    // early exit; no need to resize
	                    callback(file, false);
	                    return;
	                }
				}

                var canvas = document.createElement('canvas');
                canvas.width = avatar ? avatar : width;
                canvas.height = avatar ? avatar : height;

                var ctx = canvas.getContext('2d');
				ctx.imageSmoothingEnabled = true;
				ctx.imageSmoothingQuality = 'high';
				if (avatar) {
					ctx.drawImage(image, ...getWidthHeight(image, 90));
				} else {
					ctx.drawImage(image, 0, 0, width, height);
				}

                if (hasToBlobSupport) {
                    canvas.toBlob(function (blob) {
                        callback(blob, true);
                    }, file.type);
                } else {
                    var blob = ImageTools._toBlob(canvas, file.type);
                    callback(blob, true);
                }
            };
            ImageTools._loadImage(image, file);

            return true;
        }
    }, {
        key: '_toBlob',
        value: function _toBlob(canvas, type) {
            var dataURI = canvas.toDataURL(type);
            var dataURIParts = dataURI.split(',');
            var byteString = undefined;
            if (dataURIParts[0].indexOf('base64') >= 0) {
                // Convert base64 to raw binary data held in a string:
                byteString = atob(dataURIParts[1]);
            } else {
                // Convert base64/URLEncoded data component to raw binary data:
                byteString = decodeURIComponent(dataURIParts[1]);
            }
            var arrayBuffer = new ArrayBuffer(byteString.length);
            var intArray = new Uint8Array(arrayBuffer);

            for (var i = 0; i < byteString.length; i += 1) {
                intArray[i] = byteString.charCodeAt(i);
            }

            var mimeString = dataURIParts[0].split(':')[1].split(';')[0];
            var blob = null;

            if (hasBlobConstructor) {
                blob = new Blob([hasArrayBufferViewSupport ? intArray : arrayBuffer], { type: mimeString });
            } else {
                var bb = new BlobBuilder();
                bb.append(arrayBuffer);
                blob = bb.getBlob(mimeString);
            }

            return blob;
        }
    }, {
        key: '_loadImage',
        value: function _loadImage(image, file, callback) {
            if (typeof URL === 'undefined') {
                var reader = new FileReader();
                reader.onload = function (evt) {
                    image.src = evt.target.result;
                    if (callback) {
                        callback();
                    }
                };
                reader.readAsDataURL(file);
            } else {
                image.src = URL.createObjectURL(file);
                if (callback) {
                    callback();
                }
            }
        }
    }, {
        key: 'isSupported',
        value: function isSupported() {
            return typeof HTMLCanvasElement !== 'undefined' && hasBlobSupport && hasReaderSupport;
        }
    }]);

    return ImageTools;
})();

exports['default'] = ImageTools;
module.exports = exports['default'];

@dcollien
Copy link
Author

I'm just using this now: http://nodeca.github.io/pica/demo/

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