28

I have the based64 encoded code of an image. Now I want to reduce the size and quality of the image. How can I do this in JavaScript or jQuery?

Resolve here is the working code : Index.php Here is javascript code which worked form me

<html>
<head>
<title>JavaScript Image Resize</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style>
body {
    font-size: 16px;
    font-family: Arial;
}

</style>
<script type="text/javascript">
function _resize(img, maxWidth, maxHeight) 
{
    var ratio = 1;
    var canvas = document.createElement("canvas");
    canvas.style.display="none";
    document.body.appendChild(canvas);

    var canvasCopy = document.createElement("canvas");
    canvasCopy.style.display="none";
    document.body.appendChild(canvasCopy);

    var ctx = canvas.getContext("2d");
    var copyContext = canvasCopy.getContext("2d");

        if(img.width > maxWidth)
                ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
                ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
try {
        copyContext.drawImage(img, 0, 0);
} catch (e) { 
    document.getElementById('loader').style.display="none";
    alert("There was a problem - please reupload your image");
    return false;
}
        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        // the line to change
        //ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
        // the method signature you are using is for slicing
        ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height);
        var dataURL = canvas.toDataURL("image/png");
        document.body.removeChild(canvas);
        document.body.removeChild(canvasCopy);

        return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");


};

function resize() { 
    var photo = document.getElementById("photo");

    if(photo.files!=undefined){ 
        var loader = document.getElementById("loader");
        loader.style.display = "inline";
        var file  = photo.files[0];
        document.getElementById("orig").value = file.fileSize;
        var preview = document.getElementById("preview");
        var r = new FileReader();
        r.onload = (function(previewImage) { 
            return function(e) { 
                var maxx = 500;
                var maxy = 500;
                previewImage.src = e.target.result; 
                var k = _resize(previewImage, maxx, maxy);
                if(k!=false) { 
                document.getElementById('base64').value= k;
                document.getElementById('upload').submit();
                } else {
                alert('problem - please attempt to upload again');
                }
            }; 
        })(preview);
        r.readAsDataURL(file);
    } else {
        alert("Seems your browser doesn't support resizing");
    }
    return false;
}

</script>
</head>
<body>

<div align="center">
<h2>Image Resize Demo</h2>

    <input type="file" name="photo" id="photo">
    <br> 
    <br>    
    <input type="button" onClick="resize();" value="Resize">
    <img src="loader.gif" id="loader" />
    <img src="" alt="Image preview" id="preview">
   <form name="upload" id="upload" method='post' action='show.php'>
        <textarea name="base64" id="base64" rows='10' cols='90'></textarea>
        <input type="hidden" id="orig" name="orig" value=""/>
   </form>
</div>

</body>
</html>

Show.php file

<html>
<head>
<title>JavaScript file upload</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style>
body {
    font-size: 16px;
    font-family: Arial;
}
#preview {
    display:none;
}
#base64 {
    display:none;
}
</style>
</head>
<body>
<?php
$base64size = strlen($_POST['base64']);
$f = base64_decode($_POST['base64']);
$name = microtime(true).".png";
file_put_contents("./$name", $f);
#header("Content-type: image/png");
#header("Content-Disposition: attachment; filename=\"shrunk.png\"");
#echo $f;
#die();
?>
<h2>Shrunk file</h2>
<p>Original file was: <?=$_POST['orig'];?> bytes</p>
<p>Transmitted size was: <?=$base64size;?> bytes (due to base64)</p>
<p>New file is: <?=filesize("./$name");?> bytes</p>
<p><img src="<?=$name;?>"/></p>
</body>
</html>
Pradeep Jaiswar
  • 1,785
  • 7
  • 27
  • 48
  • Is there a solution with ruby,ruby on rails, php? – John Smith Dec 04 '13 at 15:39
  • @PradeepJaiswar Thanks for the solution, but it's better to post a [self-answer](https://stackoverflow.com/help/self-answer) than edit your solution into the question. – ggorlen Mar 11 '21 at 19:03

4 Answers4

40

You can use canvas, put image into it, scale it and get image src with new base64 code.

Here's the function doing that, it returns promise object, as image needs to be loaded (cached) first before drawing canvas out of it and getting its encoded src.

function resizeBase64Img(base64, width, height) {
    var canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    var context = canvas.getContext("2d");
    var deferred = $.Deferred();
    $("<img/>").attr("src", "data:image/gif;base64," + base64).load(function() {
        context.scale(width/this.width,  height/this.height);
        context.drawImage(this, 0, 0); 
        deferred.resolve($("<img/>").attr("src", canvas.toDataURL()));               
    });
    return deferred.promise();    
}

Can be used like this:

resizeBase64Img(oldBase64, 100, 100).then(function(newImg){
    $("body").append(newImg);
});

here's the jsfiddle

paulitto
  • 4,585
  • 2
  • 22
  • 28
  • 1
    How can I cast the resized image to a File object? – Caio Saldanha Jul 03 '18 at 21:05
  • 1
    This is nice but one note: `...).load(function()...` should now be `....).on('load', function()...` https://stackoverflow.com/questions/38871753/uncaught-typeerror-a-indexof-is-not-a-function-error-when-opening-new-foundat – Worm Feb 12 '20 at 16:55
  • `.load()` and `.on('load')` is not the same! – Jonathan May 12 '23 at 11:47
20

A non-jquery solution based on @paulitto 's answer for future googlers like me:

/**
 * Resize a base 64 Image
 * @param {String} base64 - The base64 string (must include MIME type)
 * @param {Number} newWidth - The width of the image in pixels
 * @param {Number} newHeight - The height of the image in pixels
 */
function resizeBase64Img(base64, newWidth, newHeight) {
    return new Promise((resolve, reject)=>{
        var canvas = document.createElement("canvas");
        canvas.style.width = newWidth.toString()+"px";
        canvas.style.height = newHeight.toString()+"px";
        let context = canvas.getContext("2d");
        let img = document.createElement("img");
        img.src = base64;
        img.onload = function () {
            context.scale(newWidth/img.width,  newHeight/img.height);
            context.drawImage(img, 0, 0); 
            resolve(canvas.toDataURL());               
        }
    });
}

Use it like:

resizeBase64Img(basedata, 50, 50).then((result)=>{
    console.log("After resize: "+result);
});

Note that this function returns a base64 string. To get an <img>, you can use something like

resizeBase64Img(basedata, 50, 50).then((result)=>{
    let img = document.createElement("img");
    img.onload = ()=>{
        // do something with the img
    }
    img.src = result;
});

Example:

function resizeBase64Img(base64, newWidth, newHeight) {
    return new Promise((resolve, reject)=>{
        var canvas = document.createElement("canvas");
        canvas.width = newWidth;
        canvas.height = newHeight;
        let context = canvas.getContext("2d");
        let img = document.createElement("img");
        img.src = base64;
        img.onload = function () {
            context.scale(newWidth/img.width,  newHeight/img.height);
            context.drawImage(img, 0, 0); 
            resolve(canvas.toDataURL());               
        }
    });
}

let base64 = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCgRXhpZgAATU0AKgAAAAgABQEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAAEyAAIAAAAUAAAAWodpAAQAAAABAAAAbgAAAAAAAABgAAAAAQAAAGAAAAABMjAyMDoxMjozMSAwOTozODoxNwAAA6ABAAMAAAABAAEAAKACAAMAAAABAEwAAKADAAMAAAABADIAAAAAAAD/4QyJaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjUuMCI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjAtMTItMzFUMDk6Mzg6MTctMDU6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDIwLTEyLTMxVDA5OjM4OjE3LTA1OjAwIj4gPGRjOnRpdGxlPiA8cmRmOkFsdD4gPHJkZjpsaSB4bWw6bGFuZz0ieC1kZWZhdWx0Ij5DaXR5c2NhcGVfYW5kX3NreWxpbmVfYnlfdGhlX0NvcGVuaGFnZW5fTGFrZXMsX0Rlbm1hcmtfLV8oMzYwMTgxMDk5NTYpPC9yZGY6bGk+IDwvcmRmOkFsdD4gPC9kYzp0aXRsZT4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHhtcE1NOmFjdGlvbj0icHJvZHVjZWQiIHhtcE1NOnNvZnR3YXJlQWdlbnQ9IkFmZmluaXR5IFBob3RvIChOb3YgIDYgMjAyMCkiIHhtcE1NOndoZW49IjIwMjAtMTItMzFUMDk6MzY6MjktMDU6MDAiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InByb2R1Y2VkIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAoTm92ICA2IDIwMjApIiBzdEV2dDp3aGVuPSIyMDIwLTEyLTMxVDA5OjM4OjE3LTA1OjAwIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+/+0BkFBob3Rvc2hvcCAzLjAAOEJJTQPtAAAAAAAQAGAAAAABAAIAYAAAAAEAAjhCSU0D8gAAAAAACgAA////////AAA4QklNBAQAAAAAAFQcAVoAAxslRxwCAAACAAQcAgUAQENpdHlzY2FwZV9hbmRfc2t5bGluZV9ieV90aGVfQ29wZW5oYWdlbl9MYWtlcyxfRGVubWFya18tXygzNjAxODE4QklNBAYAAAAAAAcACAAAAAMBADhCSU0ECAAAAAAAEAAAAAEAAAJAAAAC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";

document.body.append("Before: ");
let before = document.createElement("img");
before.src = base64;
document.body.appendChild(before);

resizeBase64Img(base64, 20, 20).then(resized=>{
  document.body.append("After: ");
  let img = document.createElement("img");
  img.src = resized;
  document.body.appendChild(img);
});
img {
  display: block;
}
Nate Levin
  • 918
  • 9
  • 22
1

Here is an improved typescript pro version.

export type ResizeOptions = {
  newSizeOrScale: number
  sizeOrScale: 'size' | 'scale'
  target: 'width' | 'height'
}

export type ImageSizes = {
  width: number
  height: number
}





export class Img {
  /**
   *
   *
   *
   *
   * converts an image file to base64 date url (get image file from and input element with type of file)
   */
  static async toBase64(imageFile: File, onError?: (error: ProgressEvent<FileReader>) => void) {
    const toBase64 = (): Promise<string | null> => {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsDataURL(imageFile)
        reader.onload = () => resolve(<string | null>reader.result)
        reader.onerror = reject
      })
    }

    try {
      return await toBase64()
    } catch (error) {
      onError?.(error as ProgressEvent<FileReader>)
      return null
    }
  }

  /**
   *
   *
   *
   *
   * convert a base64 date url to an image element
   */
  static async base64ToImage(base64: string, onError?: (error?: Error) => void): Promise<HTMLImageElement | null> {
    const getImage = (): Promise<HTMLImageElement | null> => {
      return new Promise((resolve, reject) => {
        const image = new Image()
        image.src = base64
        image.onload = () => resolve(image)
        image.onerror = (_0, _1, _2, _3, error) => reject(error)
      })
    }

    try {
      return await getImage()
    } catch (error) {
      onError?.(error as Error | undefined)
      return null
    }
  }

  /**
   *
   *
   *
   *
   * returns the actual size of and image (image could be a base64 data url or a file)
   */
  static async getSizes(image: File | string, onError?: (error?: Error | undefined | ProgressEvent<FileReader>) => void) {
    const base64Image = typeof image === 'string' ? image : await Img.toBase64(image, onError)
    if (!base64Image) {
      return null
    }

    const getSizes = (): Promise<ImageSizes | null> => {
      return new Promise(function (resolve, reject) {
        const image = new Image()
        image.onload = () => resolve(image)
        image.onerror = (_0, _1, _2, _3, error) => reject(error)
        image.src = base64Image
      })
    }

    try {
      return await getSizes()
    } catch (error) {
      onError?.(error as Error | undefined)
      return null
    }
  }

  /**
   *
   *
   *
   *
   * it's a helper for the Img.decreaseSize static method
   * helps to calculate the new width and height of the given image on that method
   */
  private static calculateSize(defaultWidth: number, defaultHeight: number, scaleOrCustom: ResizeOptions): ImageSizes {
    const { newSizeOrScale, sizeOrScale, target } = scaleOrCustom
    let width = defaultWidth
    let height = defaultHeight

    switch (sizeOrScale) {
      case 'scale': {
        width = defaultWidth / newSizeOrScale
        height = defaultHeight / newSizeOrScale
        break
      }

      case 'size': {
        if (target === 'width') {
          if (defaultWidth > newSizeOrScale) {
            width = newSizeOrScale
            height = (defaultHeight * newSizeOrScale) / defaultWidth
          }
        } else {
          if (defaultHeight > newSizeOrScale) {
            height = newSizeOrScale
            width = (defaultWidth * newSizeOrScale) / defaultHeight
          }
        }
        break
      }
    }

    return { width, height }
  }

  /**
   *
   *
   *
   *
   * resizes an image (image could be a base64 data url or a file)
   */
  static async resize(
    image: File | string,
    scaleOrCustom: ResizeOptions = {
      newSizeOrScale: 0.5,
      sizeOrScale: 'scale',
      target: 'width',
    },
    onError?: (error?: ProgressEvent<FileReader> | Error) => void,
  ) {
    const resize = async (): Promise<string | null> => {
      return new Promise(async (resolve, reject) => {
        const _image = typeof image === 'string' ? image : await Img.toBase64(image, reject)
        if (!_image) return null

        const sizes = await Img.getSizes(_image, reject)
        if (!sizes) return

        const { width: defaultWidth, height: defaultHeight } = sizes
        const { width: newWidth, height: newHeight } = Img.calculateSize(defaultWidth, defaultHeight, scaleOrCustom)
        const canvas = document.createElement('canvas')

        canvas.width = newWidth
        canvas.height = newHeight

        const context = canvas.getContext('2d')
        if (!context) return

        const img = await Img.base64ToImage(_image, reject)
        if (!img) return

        context.drawImage(img, 0, 0, newWidth, newHeight)
        resolve(canvas.toDataURL())
      })
    }

    try {
      return await resize()
    } catch (error) {
      onError?.(error as ProgressEvent<FileReader> | Error)
      return null
    }
  }
}

qafoori
  • 781
  • 8
  • 13
0

Here's another solution, reduce width/height until it satisfies the image size.

const maxFileSize = 2 * 1024 * 1024; // 2Mb, image should have width/height which will give not more 2Mb

let base64: string = await this.resizeImage(base64);

while (getBase64Size(base64) > maxFileSize) {
    base64 = await this.resizeImage(base64);
}

// at this point, base64 will be lower than maxFileSize. You can put it in img.src

private async resizeImage(base64: string): Promise<string> {
    return new Promise((resolve) => {
        const image = document.createElement('img');

        image.src = base64;

        image.onload = () => {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            const decreaser = 0.86;

            canvas.width = image.width * decreaser;
            canvas.height = image.height * decreaser;
            ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

            resolve(canvas.toDataURL());
        };
    });
}

private getBase64Size(base64: string): number {
    return Math.ceil(base64.length * 0.73);
}

Roman
  • 85
  • 7