-
-
Save dcollien/312bce1270a5f511bf4a to your computer and use it in GitHub Desktop.
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']; |
Are there any demos of this? Thanks for sharing BTW!
Here's an updated example for TypeScript that addresses the BlobBuilder
to Blob
and fixes the problem with loss-of-quality with the resampling:
const hasBlobConstructor = typeof (Blob) !== 'undefined' && (function () {
try {
return Boolean(new Blob());
} catch (e) {
return false;
}
}());
const hasArrayBufferViewSupport = hasBlobConstructor && typeof (Uint8Array) !== 'undefined' && (function () {
try {
return new Blob([new Uint8Array(100)]).size === 100;
} catch (e) {
return false;
}
}());
const hasToBlobSupport = (typeof HTMLCanvasElement !== 'undefined' ? HTMLCanvasElement.prototype.toBlob : false);
const hasBlobSupport = (hasToBlobSupport ||
(typeof Uint8Array !== 'undefined' && typeof ArrayBuffer !== 'undefined' && typeof atob !== 'undefined'));
const hasReaderSupport = (typeof FileReader !== 'undefined' || typeof URL !== 'undefined');
export class ImageTools {
static resize(file: File, maxDimensions: { width: number, height: number }, callback) {
if (typeof maxDimensions === 'function') {
callback = maxDimensions;
maxDimensions = {
width: 640,
height: 480
};
}
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;
}
const image = document.createElement('img');
image.onload = (imgEvt) => {
let width = image.width;
let height = image.height;
let isTooLarge = false;
if (width >= height && width > maxDimensions.width) {
isTooLarge = true;
} else if (height > maxDimensions.height) {
isTooLarge = true;
}
if (!isTooLarge) {
// early exit; no need to resize
callback(file, false);
return;
}
const scaleRatio = maxDimensions.width / width;
// TODO number of resampling steps
// const steps = Math.ceil(Math.log(width / (width * scaleRatio)) / Math.log(2));
width *= scaleRatio;
height *= scaleRatio;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
(ctx as any).imageSmoothingQuality = 'high';
ctx.drawImage(image, 0, 0, width, height);
if (hasToBlobSupport) {
canvas.toBlob((blob) => {
callback(blob, true);
}, file.type);
} else {
const blob = ImageTools._toBlob(canvas, file.type);
callback(blob, true);
}
};
ImageTools._loadImage(image, file);
return true;
}
static _toBlob(canvas, type) {
const dataURI = canvas.toDataURL(type);
const 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]);
}
const arrayBuffer = new ArrayBuffer(byteString.length);
const intArray = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i += 1) {
intArray[i] = byteString.charCodeAt(i);
}
const mimeString = dataURIParts[0].split(':')[1].split(';')[0];
let blob = null;
if (hasBlobConstructor) {
blob = new Blob(
[hasArrayBufferViewSupport ? intArray : arrayBuffer],
{ type: mimeString }
);
} else {
blob = new Blob([arrayBuffer]);
}
return blob;
}
static _loadImage(image, file, callback?: any) {
if (typeof (URL) === 'undefined') {
const reader = new FileReader();
reader.onload = function (evt) {
image.src = (evt.target as any).result;
if (callback) { callback(); }
};
reader.readAsDataURL(file);
} else {
image.src = URL.createObjectURL(file);
if (callback) {
callback();
}
}
};
static _toFile = (theBlob: Blob, fileName: string): File => {
const b: any = theBlob;
b.lastModifiedDate = new Date();
b.name = fileName;
return <File>theBlob;
}
static isSupported() {
return (
(typeof (HTMLCanvasElement) !== 'undefined')
&& hasBlobSupport
&& hasReaderSupport
);
}
}
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..........
@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.
My bad, it was hot reloading. It was not doing right. After manual page refresh everything works. Thanks.
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]
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?
How should I send the resized blob to server in POST request.
I'v wrote one with orientation fix using exif, @leventengin or anyone else how looks for a solution
https://gist.github.com/SagiMedina/f00a57de4e211456225d3114fd10b0d0
@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.
Thanks! Gracias por la solución!
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'];
I'm just using this now: http://nodeca.github.io/pica/demo/
Great script. Some issues:
line 122
let bb = new BlobBuilder();
BlobBuilder is deprecated in favour of new Blob(). Change this line to
let bb = new Blob();
and the script works fine.line 57
These vars are never used.