How to compress an image via Javascript in the browser?
Asked Answered
M

15

180

TL;DR;

Is there a way to compress an image (mostly jpeg, png and gif) directly browser-side, before uploading it ? I'm pretty sure JavaScript can do this, but I can't find a way to achieve it.


Here's the full scenario I would like to implement:

  • the user goes to my website, and choose an image via an input type="file" element,
  • this image is retrieved via JavaScript, we do some verification such as correct file format, maximum file size etc,
  • if every thing is OK, a preview of the image is displayed on the page,
  • the user can do some basic operations such as rotate the image by 90°/-90°, crop it following a pre-defined ratio, etc, or the user can upload another image and return to step 1,
  • when the user is satisfied, the edited image is then compressed and "saved" locally (not saved to a file, but in the browser memory/page),-
  • the user fill a form with data like name, age etc,
  • the user click on the "Finish" button, then the form containing datas + compressed image is sent to the server (without AJAX),

The full process up to the last step should be done client side, and should be compatible on latest Chrome and Firefox, Safari 5+ and IE 8+. If possible, only JavaScript should be used (but I'm pretty sure this is not possible).

I've not code anything right now, but I've thought about it already. File reading locally is possible via File API, image previewing and editing could be done using Canvas element, but I can't find a way to do the image compression part.

According to html5please.com and caniuse.com, supporting those browser is quite hard (thanks to IE), but could be done using polyfill such as FlashCanvas and FileReader.

Actually, the goal is to reduce file size, so I see image compression as a solution. But, I know that uploaded images are going to be displayed on my website, every time at the same place, and I know the dimension of this display area (eg. 200x400). So, I could resize the image to fit those dimensions, thus reducing file size. I have no idea what would be the compression ratio for this technique.

What do you think ? Do you have any advice to tell me ? Do you know any way to compress an image browser-side in JavaScript ? Thanks for your replies.

Malvie answered 3/2, 2013 at 13:2 Comment(8)
That may help: sitelint.com/blog/…Venery
@CezaryTomczyk Sitelint seems to be good, but terrible documentation!Strutting
@AhmedSuror There is always room for improvement. Which part do you think could be improved in the documentation?Venery
@CezaryTomczyk I followed their only one-step post on their site to use it, but no luck! there is no enough docs that refer to how to implement it!Strutting
@AhmedSuror Correct me if I am wrong, but you were thinking about using it through, e.g., NPM in the project. If so, then there is already a link to that, and, fair enough, that should be stated more clearly.Venery
@CezaryTomczyk No, I'm using it in an ASP.Net core project, and I've included the script as stated in docs, but couldn't get any further!Strutting
@AhmedSuror Are there any errors in the developer console? Maybe worth submitting the issue: github.com/sitelint/optimize-image-on-the-client-side/issuesVenery
@CezaryTomczyk Thanks for your help ❤️, I'll try to open an issue.Strutting
D
249

In short:

  • Read the files using the HTML5 FileReader API with .readAsArrayBuffer
  • Create a Blob with the file data and get its url with window.URL.createObjectURL(blob)
  • Create new Image element and set it's src to the file blob url
  • Send the image to the canvas. The canvas size is set to desired output size
  • Get the scaled-down data back from canvas via canvas.toDataURL("image/jpeg",0.7) (set your own output format and quality)
  • Attach new hidden inputs to the original form and transfer the dataURI images basically as normal text
  • On backend, read the dataURI, decode from Base64, and save it

Source: code.

Deathbed answered 3/2, 2013 at 13:24 Comment(10)
Thanks a lot ! This is what I was looking for. Do you know how good the compression ratio is with this technique ?Malvie
Apart from the network transmission (you are sending Base64 encoded content, which is not the best one), the image compression alghorithm is one of the standard ones, the size depends on the quality and format you choose.Deathbed
@NicholasKyriakides I can confirm that canvas.toDataURL("image/jpeg",0.7) effectively compresses it, it saves JPEG with quality 70 (as opposed to the default, quality 100).Monro
@Monro If you can revert it back to quality 100 then it is compression, otherwise if it's a one way street and you can't get it back to quality 100, it is downscaling - Unless I got my terms and understanding mixed up, not sure about that.Archivolt
I'm sorry, I understood downscaling as just lowering the height and width, and compression as actually reducing the quality. Not sure about my terms either, but at least we agree on the contents. :)Monro
@Nicholas Kyriakides, this is not good distinction to make. Most of the codecs are not lossless, so they would fit into your "downscaling" definition (i.e. you cant revert to 100).Gregorygregrory
You should convert from Base64 on the front end, append it to the FormData and submit it in binary as a file. Then you aren't adding pointless 33% to the file size and limited by the postdata size etc.Slang
Downscaling refers to making images of a smaller size in terms of height and width. This really is compression. It's lossy compression but certainly compression. It isn't downscaling the pixels, it just nudges some of the pixels to being the same color so that the compression can hit those colors in fewer bits. JPEG has built in compression for the pixels anyway, but but in lossy mode it says that a few colors off can be called the same color. That's still compression. Downscaling with regard to graphics typically refers to a change in the actual size.Slang
I just want to say this: the file can go straight to URL.createObjectUrl() without turning the file into a blob; the file counts as a blob.Callosity
I've added a demonstrator jsfiddle for psychowood's code sample should anyone need: jsfiddle.net/Abeeee/0wxeugrt/9Enantiomorph
N
44

I see two things missing from the other answers:

  • canvas.toBlob (when available) is more performant than canvas.toDataURL, and also async.
  • the file -> image -> canvas -> file conversion loses EXIF data; in particular, data about image rotation commonly set by modern phones/tablets.

The following script deals with both points:

// From https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob, needed for Safari:
if (!HTMLCanvasElement.prototype.toBlob) {
    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
        value: function(callback, type, quality) {

            var binStr = atob(this.toDataURL(type, quality).split(',')[1]),
                len = binStr.length,
                arr = new Uint8Array(len);

            for (var i = 0; i < len; i++) {
                arr[i] = binStr.charCodeAt(i);
            }

            callback(new Blob([arr], {type: type || 'image/png'}));
        }
    });
}

window.URL = window.URL || window.webkitURL;

// Modified from https://stackoverflow.com/a/32490603, cc by-sa 3.0
// -2 = not jpeg, -1 = no data, 1..8 = orientations
function getExifOrientation(file, callback) {
    // Suggestion from http://code.flickr.net/2012/06/01/parsing-exif-client-side-using-javascript-2/:
    if (file.slice) {
        file = file.slice(0, 131072);
    } else if (file.webkitSlice) {
        file = file.webkitSlice(0, 131072);
    }

    var reader = new FileReader();
    reader.onload = function(e) {
        var view = new DataView(e.target.result);
        if (view.getUint16(0, false) != 0xFFD8) {
            callback(-2);
            return;
        }
        var length = view.byteLength, offset = 2;
        while (offset < length) {
            var marker = view.getUint16(offset, false);
            offset += 2;
            if (marker == 0xFFE1) {
                if (view.getUint32(offset += 2, false) != 0x45786966) {
                    callback(-1);
                    return;
                }
                var little = view.getUint16(offset += 6, false) == 0x4949;
                offset += view.getUint32(offset + 4, little);
                var tags = view.getUint16(offset, little);
                offset += 2;
                for (var i = 0; i < tags; i++)
                    if (view.getUint16(offset + (i * 12), little) == 0x0112) {
                        callback(view.getUint16(offset + (i * 12) + 8, little));
                        return;
                    }
            }
            else if ((marker & 0xFF00) != 0xFF00) break;
            else offset += view.getUint16(offset, false);
        }
        callback(-1);
    };
    reader.readAsArrayBuffer(file);
}

// Derived from https://stackoverflow.com/a/40867559, cc by-sa
function imgToCanvasWithOrientation(img, rawWidth, rawHeight, orientation) {
    var canvas = document.createElement('canvas');
    if (orientation > 4) {
        canvas.width = rawHeight;
        canvas.height = rawWidth;
    } else {
        canvas.width = rawWidth;
        canvas.height = rawHeight;
    }

    if (orientation > 1) {
        console.log("EXIF orientation = " + orientation + ", rotating picture");
    }

    var ctx = canvas.getContext('2d');
    switch (orientation) {
        case 2: ctx.transform(-1, 0, 0, 1, rawWidth, 0); break;
        case 3: ctx.transform(-1, 0, 0, -1, rawWidth, rawHeight); break;
        case 4: ctx.transform(1, 0, 0, -1, 0, rawHeight); break;
        case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
        case 6: ctx.transform(0, 1, -1, 0, rawHeight, 0); break;
        case 7: ctx.transform(0, -1, -1, 0, rawHeight, rawWidth); break;
        case 8: ctx.transform(0, -1, 1, 0, 0, rawWidth); break;
    }
    ctx.drawImage(img, 0, 0, rawWidth, rawHeight);
    return canvas;
}

function reduceFileSize(file, acceptFileSize, maxWidth, maxHeight, quality, callback) {
    if (file.size <= acceptFileSize) {
        callback(file);
        return;
    }
    var img = new Image();
    img.onerror = function() {
        URL.revokeObjectURL(this.src);
        callback(file);
    };
    img.onload = function() {
        URL.revokeObjectURL(this.src);
        getExifOrientation(file, function(orientation) {
            var w = img.width, h = img.height;
            var scale = (orientation > 4 ?
                Math.min(maxHeight / w, maxWidth / h, 1) :
                Math.min(maxWidth / w, maxHeight / h, 1));
            h = Math.round(h * scale);
            w = Math.round(w * scale);

            var canvas = imgToCanvasWithOrientation(img, w, h, orientation);
            canvas.toBlob(function(blob) {
                console.log("Resized image to " + w + "x" + h + ", " + (blob.size >> 10) + "kB");
                callback(blob);
            }, 'image/jpeg', quality);
        });
    };
    img.src = URL.createObjectURL(file);
}

Example usage:

inputfile.onchange = function() {
    // If file size > 500kB, resize such that width <= 1000, quality = 0.9
    reduceFileSize(this.files[0], 500*1024, 1000, Infinity, 0.9, blob => {
        let body = new FormData();
        body.set('file', blob, blob.name || "file.jpg");
        fetch('/upload-image', {method: 'POST', body}).then(...);
    });
};
Neckline answered 30/6, 2017 at 14:46 Comment(2)
ToBlob did the trick for me, creating a file and recieving in the $_FILES array on the server. Thank you!Hungerford
Looks great! But will this work on all browsers, web and mobile? (Let's ignore IE)Hayse
I
23

@PsychoWoods' answer is good. I would like to offer my own solution. This Javascript function takes an image data URL and a width, scales it to the new width, and returns a new data URL.

// Take an image URL, downscale it to the given width, and return a new image URL.
function downscaleImage(dataUrl, newWidth, imageType, imageArguments) {
    "use strict";
    var image, oldWidth, oldHeight, newHeight, canvas, ctx, newDataUrl;

    // Provide default values
    imageType = imageType || "image/jpeg";
    imageArguments = imageArguments || 0.7;

    // Create a temporary image so that we can compute the height of the downscaled image.
    image = new Image();
    image.src = dataUrl;
    oldWidth = image.width;
    oldHeight = image.height;
    newHeight = Math.floor(oldHeight / oldWidth * newWidth)

    // Create a temporary canvas to draw the downscaled image on.
    canvas = document.createElement("canvas");
    canvas.width = newWidth;
    canvas.height = newHeight;

    // Draw the downscaled image on the canvas and return the new data URL.
    ctx = canvas.getContext("2d");
    ctx.drawImage(image, 0, 0, newWidth, newHeight);
    newDataUrl = canvas.toDataURL(imageType, imageArguments);
    return newDataUrl;
}

This code can be used anywhere you have a data URL and want a data URL for a downscaled image.

Illegitimate answered 27/9, 2016 at 22:31 Comment(5)
please, can you give me more detail about this example, how to call the function and how returned the result?Yore
Here's an example: danielsadventure.info/Html/scaleimage.html Be sure to read the source of the page to see how it works.Illegitimate
web.archive.org/web/20171226190510/danielsadventure.info/Html/… For other people who wants to read the link propose by @DanielAllenLangdonEstivation
As a heads up, sometimes image.width/height will return 0 since it hasn't loaded. You might need to convert this into an async function and listen to image.onload to get the correct image with and height.Communize
-1 because as soon as image.src is assigned, the image gets loaded asynchronously. If you have an image that is larger than tiny your code will not run and won't throw any error messages. Had to figure this out the hard way.Amphi
B
18

You can take a look at image-conversion,Try it here --> demo page

enter image description here

Bethesda answered 24/12, 2018 at 2:28 Comment(6)
Please add some information about the linked resourcesSade
The demo page link is broken. You can test here: demo.wangyulue.com/image-conversionTearful
This is so simple and useful. Guys, don't even spend your time reading other answers...Renascent
It is the best plugin so far, super easy to use and powerful. This can be the best answer. Thanks for sharingBearable
still can't understand a couple of things about this, like the relation between all values like accuracy and size for example ..Turnkey
from readme. It say: if the picture size is set to 1000Kb and the accuracy is 0.9, the image with the compression result of 900Kb-1100Kb is considered acceptable;Imhoff
M
12

Try this Customizable Pure JS Sample - Compress over 90% :

   <div id="root">
        <p>Upload an image and see the result</p>
        <input id="img-input" type="file" accept="image/*" style="display:block" />
    </div>

    <script>
        const MAX_WIDTH = 320;
        const MAX_HEIGHT = 180;
        const MIME_TYPE = "image/jpeg";
        const QUALITY = 0.7;

        const input = document.getElementById("img-input");
        input.onchange = function (ev) {
            const file = ev.target.files[0]; // get the file
            const blobURL = URL.createObjectURL(file);
            const img = new Image();
            img.src = blobURL;
            img.onerror = function () {
                URL.revokeObjectURL(this.src);
                // Handle the failure properly
                console.log("Cannot load image");
            };
            img.onload = function () {
                URL.revokeObjectURL(this.src);
                const [newWidth, newHeight] = calculateSize(img, MAX_WIDTH, MAX_HEIGHT);
                const canvas = document.createElement("canvas");
                canvas.width = newWidth;
                canvas.height = newHeight;
                const ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, newWidth, newHeight);
                canvas.toBlob(
                    (blob) => {
                        // Handle the compressed image. es. upload or save in local state
                        displayInfo('Original file', file);
                        displayInfo('Compressed file', blob);
                    },
                    MIME_TYPE,
                    QUALITY
                );
                document.getElementById("root").append(canvas);
            };
        };

        function calculateSize(img, maxWidth, maxHeight) {
            let width = img.width;
            let height = img.height;

            // calculate the width and height, constraining the proportions
            if (width > height) {
                if (width > maxWidth) {
                    height = Math.round((height * maxWidth) / width);
                    width = maxWidth;
                }
            } else {
                if (height > maxHeight) {
                    width = Math.round((width * maxHeight) / height);
                    height = maxHeight;
                }
            }
            return [width, height];
        }

        // Utility functions for demo purpose

        function displayInfo(label, file) {
            const p = document.createElement('p');
            p.innerText = `${label} - ${readableBytes(file.size)}`;
            document.getElementById('root').append(p);
        }

        function readableBytes(bytes) {
            const i = Math.floor(Math.log(bytes) / Math.log(1024)),
                sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

            return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
        }
    </script>
Monolayer answered 27/8, 2021 at 16:46 Comment(1)
Best answer here so far. Clean code as wellBernetta
C
8

I find that there's simpler solution compared to the accepted answer.

  • Read the files using the HTML5 FileReader API with .readAsArrayBuffer
  • Create a Blob with the file data and get its url with window.URL.createObjectURL(blob)
  • Create new Image element and set it's src to the file blob url
  • Send the image to the canvas. The canvas size is set to desired output size
  • Get the scaled-down data back from canvas via canvas.toDataURL("image/jpeg",0.7) (set your own output format and quality)
  • Attach new hidden inputs to the original form and transfer the dataURI images basically as normal text
  • On backend, read the dataURI, decode from Base64, and save it

As per your question:

Is there a way to compress an image (mostly jpeg, png and gif) directly browser-side, before uploading it

My solution:

  1. Create a blob with the file directly with URL.createObjectURL(inputFileElement.files[0]).

  2. Same as accepted answer.

  3. Same as accepted answer. Worth mentioning that, canvas size is necessary and use img.width and img.height to set canvas.width and canvas.height. Not img.clientWidth.

  4. Get the scale-down image by canvas.toBlob(callbackfunction(blob){}, 'image/jpeg', 0.5). Setting 'image/jpg' has no effect. image/png is also supported. Make a new File object inside the callbackfunction body with let compressedImageBlob = new File([blob]).

  5. Add new hidden inputs or send via javascript . Server doesn't have to decode anything.

Check https://javascript.info/binary for all information. I came up the solution after reading this chapter.


Code:

    <!DOCTYPE html>
    <html>
    <body>
    <form action="upload.php" method="post" enctype="multipart/form-data">
      Select image to upload:
      <input type="file" name="fileToUpload" id="fileToUpload" multiple>
      <input type="submit" value="Upload Image" name="submit">
    </form>
    </body>
    </html>

This code looks far less scary than the other answers..

Update:

One has to put everything inside img.onload. Otherwise canvas will not be able to get the image's width and height correctly as the time canvas is assigned.

    function upload(){
        var f = fileToUpload.files[0];
        var fileName = f.name.split('.')[0];
        var img = new Image();
        img.src = URL.createObjectURL(f);
        img.onload = function(){
            var canvas = document.createElement('canvas');
            canvas.width = img.width;
            canvas.height = img.height;
            var ctx = canvas.getContext('2d');
            ctx.drawImage(img, 0, 0);
            canvas.toBlob(function(blob){
                    console.info(blob.size);
                    var f2 = new File([blob], fileName + ".jpeg");
                    var xhr = new XMLHttpRequest();
                    var form = new FormData();
                    form.append("fileToUpload", f2);
                    xhr.open("POST", "upload.php");
                    xhr.send(form);
            }, 'image/jpeg', 0.5);
        }
    }

3.4MB .png file compression test with image/jpeg argument set.

    |0.9| 777KB |
    |0.8| 383KB |
    |0.7| 301KB |
    |0.6| 251KB |
    |0.5| 219kB |
Cholecyst answered 12/8, 2020 at 14:37 Comment(2)
I like your solution, but How can we preserve EXIF data?Vikkivikky
I used this function but was getting application/octet-stream instead of image/jpeg in the server. To solve it I sent the blob itself instead of new File(...)Extractive
T
7

For Moderm browser use createImageBitmap() instead of img.onload

async function compressImage(blobImg, percent) {
  let bitmap = await createImageBitmap(blobImg);
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext("2d");
  canvas.width = bitmap.width;
  canvas.height = bitmap.height;
  ctx.drawImage(bitmap, 0, 0);
  let dataUrl = canvas.toDataURL("image/jpeg", percent/100);
  return dataUrl;
}

inputImg.addEventListener('change', async(e) => {
  let img = e.target.files[0];
  console.log('File Name: ', img.name)
  console.log('Original Size: ', img.size.toLocaleString())
  
  let imgCompressed = await compressImage(img, 75) // set to 75%
  let compSize = atob(imgCompressed.split(",")[1]).length;
  console.log('Compressed Size: ', compSize.toLocaleString())
  //console.log(imgCompressed)
})
<input type="file" id="inputImg">
Tine answered 16/9, 2022 at 11:44 Comment(0)
A
5

I had an issue with the downscaleImage() function posted above by @daniel-allen-langdon in that the image.width and image.height properties are not available immediately because the image load is asynchronous.

Please see updated TypeScript example below that takes this into account, uses async functions, and resizes the image based on the longest dimension rather than just the width

function getImage(dataUrl: string): Promise<HTMLImageElement> 
{
    return new Promise((resolve, reject) => {
        const image = new Image();
        image.src = dataUrl;
        image.onload = () => {
            resolve(image);
        };
        image.onerror = (el: any, err: ErrorEvent) => {
            reject(err.error);
        };
    });
}

export async function downscaleImage(
        dataUrl: string,  
        imageType: string,  // e.g. 'image/jpeg'
        resolution: number,  // max width/height in pixels
        quality: number   // e.g. 0.9 = 90% quality
    ): Promise<string> {

    // Create a temporary image so that we can compute the height of the image.
    const image = await getImage(dataUrl);
    const oldWidth = image.naturalWidth;
    const oldHeight = image.naturalHeight;
    console.log('dims', oldWidth, oldHeight);

    const longestDimension = oldWidth > oldHeight ? 'width' : 'height';
    const currentRes = longestDimension == 'width' ? oldWidth : oldHeight;
    console.log('longest dim', longestDimension, currentRes);

    if (currentRes > resolution) {
        console.log('need to resize...');

        // Calculate new dimensions
        const newSize = longestDimension == 'width'
            ? Math.floor(oldHeight / oldWidth * resolution)
            : Math.floor(oldWidth / oldHeight * resolution);
        const newWidth = longestDimension == 'width' ? resolution : newSize;
        const newHeight = longestDimension == 'height' ? resolution : newSize;
        console.log('new width / height', newWidth, newHeight);

        // Create a temporary canvas to draw the downscaled image on.
        const canvas = document.createElement('canvas');
        canvas.width = newWidth;
        canvas.height = newHeight;

        // Draw the downscaled image on the canvas and return the new data URL.
        const ctx = canvas.getContext('2d')!;
        ctx.drawImage(image, 0, 0, newWidth, newHeight);
        const newDataUrl = canvas.toDataURL(imageType, quality);
        return newDataUrl;
    }
    else {
        return dataUrl;
    }

}
Araceli answered 27/7, 2018 at 2:46 Comment(1)
I would add the explanation of quality, resolution and imageType (format of this)Mansion
L
4

Compressor.js

https://github.com/fengyuanchen/compressorjs

import axios from 'axios';
import Compressor from 'compressorjs';

document.getElementById('file').addEventListener('change', (e) => {
  const file = e.target.files[0];

  if (!file) {
    return;
  }

  new Compressor(file, {
    quality: 0.6,

    // The compression process is asynchronous,
    // which means you have to access the `result` in the `success` hook function.
    success(result) {
      const formData = new FormData();

      // The third parameter is required for server
      formData.append('file', result, result.name);

      // Send the compressed image file to server with XMLHttpRequest.
      axios.post('/path/to/upload', formData).then(() => {
        console.log('Upload success');
      });
    },
    error(err) {
      console.log(err.message);
    },
  });
});
Lecompte answered 27/4, 2021 at 7:55 Comment(1)
"cannot read properties of undefined (reading '0')" Thanks a lot! I didn't know the third parameter was required for the server, I was getting this error before and now is fixed, thanks!Richel
U
3

Edit: As per the Mr Me comment on this answer, it looks like compression is now available for JPG/WebP formats ( see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL ).

As far as I know, you cannot compress images using canvas, instead, you can resize it. Using canvas.toDataURL will not let you choose the compression ratio to use. You can take a look at canimage that does exactly what you want : https://github.com/nfroidure/CanImage/blob/master/chrome/canimage/content/canimage.js

In fact, it's often sufficient to just resize the image to decrease it's size but if you want to go further, you'll have to use newly introduced method file.readAsArrayBuffer to get a buffer containing the image data.

Then, just use a DataView to read it's content according to the image format specification (http://en.wikipedia.org/wiki/JPEG or http://en.wikipedia.org/wiki/Portable_Network_Graphics).

It'll be hard to deal with image data compression, but it is worse a try. On the other hand, you can try to delete the PNG headers or the JPEG exif data to make your image smaller, it should be easier to do so.

You'll have to create another DataWiew on another buffer and fill it with the filtered image content. Then, you'll just have to encode you're image content to DataURI using window.btoa.

Let me know if you implement something similar, will be interesting to go through the code.

Unto answered 26/5, 2013 at 15:48 Comment(1)
Perhaps something has changed since you posted this, but the second argument to that canvas.toDataURL function you mentioned is the amount of compression you want to apply.Premature
O
3

I used the following package: https://www.npmjs.com/package/browser-image-compression

npm install browser-image-compression
or
yarn add browser-image-compression

Then just following the docs:

import imageCompression from 'browser-image-compression';
const options = {
 maxSizeMB: 0.5, // pretty much self-explanatory
 maxWidthOrHeight: 500, // apparently px
}

imageCompression(file, options)
            .then(function(compressedFile) {
                console.log(
                    "compressedFile instanceof Blob",
                    compressedFile instanceof Blob
                ); // true
                console.log(
                    `compressedFile size ${compressedFile.size /
                        1024 /
                        1024} MB`
                ); // smaller than maxSizeMB

                return uploader(compressedFile); // code to actual upload, in my case uploader() is a function to upload to Firebase storage.
            })

Just in case if you were curios about the uploader(), here's the code of it:

import { initializeApp } from "firebase/app";

const firebaseConfig = {
    // your config
};

initializeApp(firebaseConfig);
import { getStorage, ref, uploadBytes, getDownloadURL } from "firebase/storage";
const storage = getStorage();
const sRef = ref(storage);

const uploader = async (file) => {
        /* uploads to root */

        // const imageRef = ref(sRef, file.name);
        // console.log(imageRef);
        // await uploadBytes(imageRef, file).then((snapshot) => {
        //  console.log("Uploaded a blob or file!", snapshot);
        // });

        /* upload to folder 'techs/' */
        const folderRef = ref(sRef, "techs/" + file.name);
        await uploadBytes(folderRef, file);

        // get URL
        const url = await getDownloadURL(ref(storage, folderRef));

        console.log("url: ", url);
        return url;
    };

Odeliaodelinda answered 30/11, 2021 at 16:40 Comment(0)
B
2

You can compress an image using the HTML <canvas> element:

function compressImage(imgToCompress, resizingFactor, quality) {
  // resizing the image
  const canvas = document.createElement("canvas");
  const context = canvas.getContext("2d");
  
  const originalWidth = imgToCompress.width;
  const originalHeight = imgToCompress.height;
  
  const canvasWidth = originalWidth * resizingFactor;
  const canvasHeight = originalHeight * resizingFactor;
  
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;
  
  context.drawImage(
    imgToCompress,
    0,
    0,
    originalWidth * resizingFactor,
    originalHeight * resizingFactor
  );
  
  // reducing the quality of the image
  canvas.toBlob(
    (blob) => {
      if (blob) {
        // showing the compressed image
        resizedImage.src = URL.createObjectURL(resizedImageBlob);
      }
    },
    "image/jpeg",
    quality
  );
}

See this blog post for an in depth explanation: https://img.ly/blog/how-to-compress-an-image-before-uploading-it-in-javascript/

Balch answered 7/4, 2022 at 12:39 Comment(0)
M
1

i improved the function a head to be this :

var minifyImg = function(dataUrl,newWidth,imageType="image/jpeg",resolve,imageArguments=0.7){
    var image, oldWidth, oldHeight, newHeight, canvas, ctx, newDataUrl;
    (new Promise(function(resolve){
      image = new Image(); image.src = dataUrl;
      log(image);
      resolve('Done : ');
    })).then((d)=>{
      oldWidth = image.width; oldHeight = image.height;
      log([oldWidth,oldHeight]);
      newHeight = Math.floor(oldHeight / oldWidth * newWidth);
      log(d+' '+newHeight);

      canvas = document.createElement("canvas");
      canvas.width = newWidth; canvas.height = newHeight;
      log(canvas);
      ctx = canvas.getContext("2d");
      ctx.drawImage(image, 0, 0, newWidth, newHeight);
      //log(ctx);
      newDataUrl = canvas.toDataURL(imageType, imageArguments);
      resolve(newDataUrl);
    });
  };

the use of it :

minifyImg(<--DATAURL_HERE-->,<--new width-->,<--type like image/jpeg-->,(data)=>{
   console.log(data); // the new DATAURL
});

enjoy ;)

Mannino answered 13/10, 2019 at 10:36 Comment(0)
E
1

When the image comes from an input and you need a base64 string you can try this:

async function compressImage(input, maxWidth, maxHeight, quality) {
  const file = input.files[0];
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = event => {
      const img = new Image();
      img.src = event.target.result;
      img.onload = () => {
        let width = img.width;
        let height = img.height;

        if (width > height) {
          if (width > maxWidth) {
            height *= maxWidth / width;
            width = maxWidth;
          }
        } else {
          if (height > maxHeight) {
            width *= maxHeight / height;
            height = maxHeight;
          }
        }

        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, width, height);
        const base64String = canvas.toDataURL('image/jpeg', quality);
        resolve(base64String);
      };
    };
    reader.onerror = error => reject(error);
  });
}

Entry answered 16/3, 2023 at 22:22 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Katzen
F
0

For JPG Image compression you can use the best compression technique called JIC (Javascript Image Compression)This will definitely help you -->https://github.com/brunobar79/J-I-C

Franek answered 31/1, 2017 at 7:20 Comment(1)
without reducing the qualityFranek

© 2022 - 2024 — McMap. All rights reserved.