Resize a Base-64 image in JavaScript without using canvas

JavascriptCanvasResizeHtml5 CanvasBase64

Javascript Problem Overview


I need a way to resize pictures in JavaScript without using a HTML element.

My mobile HTML app captures photos and then converts them to base64 strings. Thereafter I need to resize them before they are sent to the API in order to save storage space.

I'm looking for a different and more suitable way for resizing than using a canvas element, is there a way?

Javascript Solutions


Solution 1 - Javascript

A way to avoid the main HTML to be affected is to create an off-screen canvas that is kept out of the DOM-tree.

This will provide a bitmap buffer and native compiled code to encode the image data. It is straight forward to do:

function imageToDataUri(img, width, height) {

    // create an off-screen canvas
    var canvas = document.createElement('canvas'),
        ctx = canvas.getContext('2d');

    // set its dimension to target size
    canvas.width = width;
    canvas.height = height;

    // draw source image into the off-screen canvas:
    ctx.drawImage(img, 0, 0, width, height);

    // encode image to data-uri with base64 version of compressed image
    return canvas.toDataURL();
}

If you want to produce a different format than PNG (default) just specify the type like this:

return canvas.toDataURL('image/jpeg', quality);  // quality = [0.0, 1.0]

Worth to note that CORS restrictions applies to toDataURL().

If your app is giving only base64 encoded images (I assume they are data-uri's with base64 data?) then you need to "load" the image first:

var img = new Image;

img.onload = resizeImage;
img.src = originalDataUriHere;

function resizeImage() {
    var newDataUri = imageToDataUri(this, targetWidth, targetHeight);
    // continue from here...
}

If the source is pure base-64 string simply add a header to it to make it a data-uri:

function base64ToDataUri(base64) {
    return 'data:image/png;base64,' + base64;
}

Just replace the image/png part with the type the base64 string represents (ie. make it an optional argument).

Solution 2 - Javascript

Ken's answer is the right answer, but his code doesn't work. I made some adjustments on it and it now works perfectly. To resize a Data URI :

// Takes a data URI and returns the Data URI corresponding to the resized image at the wanted size.
function resizedataURL(datas, wantedWidth, wantedHeight)
    {
        // We create an image to receive the Data URI
        var img = document.createElement('img');
    
        // When the event "onload" is triggered we can resize the image.
        img.onload = function()
            {        
                // We create a canvas and get its context.
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');
        
                // We set the dimensions at the wanted size.
                canvas.width = wantedWidth;
                canvas.height = wantedHeight;
                
                // We resize the image with the canvas method drawImage();
                ctx.drawImage(this, 0, 0, wantedWidth, wantedHeight);

                var dataURI = canvas.toDataURL();

                /////////////////////////////////////////
                // Use and treat your Data URI here !! //
                /////////////////////////////////////////
            };
        
        // We put the Data URI in the image's src attribute
        img.src = datas;
    }
// Use it like that : resizedataURL('yourDataURIHere', 50, 50);

Solution 3 - Javascript

Pierrick Martellière is far the best answer, I just wanted to point that you should implement that with a async function. Once that, you would be able to do something like:

var newDataUri = await resizedataURL(datas,600,600);

This will wait for the result of the function before going to the next step. It's a cleaner way to write code. Here is the function from Pierrick with the little edit:

// Takes a data URI and returns the Data URI corresponding to the resized image at the wanted size.
function resizedataURL(datas, wantedWidth, wantedHeight){
    return new Promise(async function(resolve,reject){

        // We create an image to receive the Data URI
        var img = document.createElement('img');

        // When the event "onload" is triggered we can resize the image.
        img.onload = function()
        {        
            // We create a canvas and get its context.
            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');

            // We set the dimensions at the wanted size.
            canvas.width = wantedWidth;
            canvas.height = wantedHeight;

            // We resize the image with the canvas method drawImage();
            ctx.drawImage(this, 0, 0, wantedWidth, wantedHeight);

            var dataURI = canvas.toDataURL();

            // This is the return of the Promise
            resolve(dataURI);
        };

        // We put the Data URI in the image's src attribute
        img.src = datas;
    
    })
}// Use it like : var newDataURI = await resizedataURL('yourDataURIHere', 50, 50);

For more details you can check MDN Docs : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise

Solution 4 - Javascript

Yes, you can. These solutions good for resizing not just converting image to base64.

  1. You can convert js file to image bitmap by jpg-js.And you can resize only by this lib, but in a case of resizing from very large image to very small, quality will be very bad.Best way for high-res images is to convert file to bitmap by jpg-js and then resize this bitmap by Pica lib.
  2. You can get image data from a file by jpg-js (or draw an image on canvas)and then resize canvasImageData by resizing lib pica. (good for High-resolution images, without canvas size restriction)
  3. You can use offscreen canvas, without attaching the canvas to a body, and resize an image. This solution will be faster but will be the worse solution for high-resolution images, for example 6000x6000 pixels. In that case, result canvas can be with bad quality or just empty, or browser can fall with memory limit exception. (good for normal and small images)

Jpg-js and Pica will not use dom elements at all. These libs are working only with image data, without dom elements (canvas and image).

About the canvas, size restriction see this post

Solution 5 - Javascript

You can use a raw Base64 image manipulation as shown here.

Looks difficult ( and for png only ) and why many choose to use canvas

http://blog.calyptus.eu/seb/2009/05/png-parser-in-javascript/

Solution 6 - Javascript

I think this method is best way for this solution.

function base64Resize(sourceBase64, scale , callBack) {

    const _scale = scale;
    var img = document.createElement('img');
    img.setAttribute("src", sourceBase64);

    img.onload = () => {
        var canvas = document.createElement('canvas');
        canvas.width = img.width * _scale;
        canvas.height = img.height * _scale;

        var ctx = canvas.getContext("2d");
        var cw = canvas.width;
        var ch = canvas.height;
        var maxW = img.width * _scale;
        var maxH = img.height * _scale;

        var iw = img.width;
        var ih = img.height;
        var scl = Math.min((maxW / iw), (maxH / ih));
        var iwScaled = iw * scl;
        var ihScaled = ih * scl;
        canvas.width = iwScaled;
        canvas.height = ihScaled;
        ctx.drawImage(img, 0, 0, iwScaled, ihScaled);
        const newBase64 = canvas.toDataURL("image/jpeg", scl);

        callBack(newBase64);
    }
}

Important point is that you should use img.onload event.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrilarView Question on Stackoverflow
Solution 1 - Javascriptuser1693593View Answer on Stackoverflow
Solution 2 - JavascriptPierrick MartellièreView Answer on Stackoverflow
Solution 3 - Javascriptuser8094363View Answer on Stackoverflow
Solution 4 - JavascriptAlex NikulinView Answer on Stackoverflow
Solution 5 - Javascriptuser3105359View Answer on Stackoverflow
Solution 6 - JavascriptMojtabaView Answer on Stackoverflow