Cropper: Large images have their file size dramatically increased

Created on 9 Dec 2015  ·  21Comments  ·  Source: fengyuanchen/cropper

Cropper seems to have issues with larger images. I am attaching an example image. This file is <8
MB on disk but when cropper receives the file, even if I make no changes, the filesize within Javascript is >29MB. When I try to post this data to a webserver, it takes forever to upload since it's such a big file size, and it also causes issues on the server side with resource limits.

In the demo (http://fengyuanchen.github.io/cropper/) when I upload this file via 'Blob URL' and expand the crop box to the entire image, I cannot even download the image via the 'Get Cropped Canvas' feature.
bad-photo

Most helpful comment

I found that toDataURL generated a rather large PNG file by default. When I used toDataURL("image/jpeg",0.9") the file size is much more reasonable.

All 21 comments

You might need set the checkOrientation option to false.

I configured checkOrientation to false and attempted to upload the image again. It still is presented in Javascript and to the webserver as 29.2MB of data, when it should only be 7.88MB.

Here is another image, again a large image, with no EXIF data that has the same problem. On disk it is only 592KB, but in cropper somehow modifies it to over 10MB.

andromeda

are you using toDataURL? I'm experiencing the same issue when using a quality of 1.0

Yes, I am using toDataURL

var blob = $img.cropper('getCroppedCanvas').toDataURL();

The only configuration set manually, aside from checkOrientation, is autoCropArea which is set to 1. Everything else is default.

@ngaugler You might need to limit the dimension of the cropped canvas as:

$().cropper('getCroppedCanvas', {
  width: 960
});

I found that toDataURL generated a rather large PNG file by default. When I used toDataURL("image/jpeg",0.9") the file size is much more reasonable.

Yeah I've found if you convert to the same file type e.g. toDataURL(file.type, 1.0) that you pretty much get lossless conversion for the area you cropped (file size shouldn't get any larger if you convert to the same type). I'm unclear as to how any number less than 1.0 would work. After reading the spec I was even more confused (╯°□°)╯︵ ┻━┻

Ok, so toDataURL converting it from a JPG to a PNG is what caused the file size to increase dramatically. Since the file.type is pulled from the extension of the file, and not what the actual mimeType is, I wrote some code to pull the magic numbers out of the content. I haven't tested the non Base64 code, but in theory, it should work.

First, the function to parse the dataURL data to a mime type:

    function dataURLtoMimeType(dataURL) {
        var BASE64_MARKER = ';base64,';
        var data;

        if (dataURL.indexOf(BASE64_MARKER) == -1) {
            var parts = dataURL.split(',');
            var contentType = parts[0].split(':')[1];
            data = decodeURIComponent(parts[1]);
        } else {
            var parts = dataURL.split(BASE64_MARKER);
            var contentType = parts[0].split(':')[1];
            var raw = window.atob(parts[1]);
            var rawLength = raw.length;

            data = new Uint8Array(rawLength);

            for (var i = 0; i < rawLength; ++i) {
                data[i] = raw.charCodeAt(i);
            }
        }

        var arr = data.subarray(0, 4);
        var header = "";
        for(var i = 0; i < arr.length; i++) {
            header += arr[i].toString(16);
        }
        switch (header) {
            case "89504e47":
                mimeType = "image/png";
                break;
            case "47494638":
                mimeType = "image/gif";
                break;
            case "ffd8ffe0":
            case "ffd8ffe1":
            case "ffd8ffe2":
                mimeType = "image/jpeg";
                break;
            default:
                mimeType = ""; // Or you can use the blob.type as fallback
                break;
        }

        return mimeType;
    }

In my FileReader I have:

    var reader = new FileReader();
    reader.onloadend = function (e) {
        Find the true mime type
        mimeType = dataURLtoMimeType(reader.result);
    };

Finally, when I convert from cropper toDataURL, I just use the mimeType from earlier.

var blob = $img.cropper('getCroppedCanvas').toDataURL(mimeType);

It's a shame cropper can't handle all of this work for the user. It's alot of code that really shouldn't need to be on every web page that uses toDataURL and cropper.

Looks like a bit of overkill there - after you register the reader.onload function are you calling something like reader.readAsDataURL(file)? You should be able to get the mime type from file.type. Hope this helps :smiley:

It's my understanding that file.type comes from the extension of the uploaded file and does not determine the actual file type based on the contents of the file. When I took the large JPEG above and renamed it to a PNG, the metdata from reader shows it as a PNG, not a JPEG.

Good to know... then thanks for the useful function!

for anyone who stumbles on this in the future, here is a helpful reference: http://stackoverflow.com/questions/18299806/how-to-check-file-mime-type-with-javascript-before-upload

this is chinese, 不知道你们能不能看的懂,应该是cropper调用getCroppedCanvas方法把图片的“位深度”改成了32(不管原来的位深度是多少,统一改成了32,如果原来位深度是32的,生成的图片大小变化不大)。
image

HTMLCanvasElement has toBlob function which takes in mimeType and quality as argument and returns a blob to callback function. I guess this will solve this issue for most cases.
Refer to https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob for more information.

For older browser, please see polyfill section.

If the source image is a JPEG image, then:

$('img').cropper('getCroppedCanvas').toDataURL() // bad, the size will be larger
$('img').cropper('getCroppedCanvas').toDataURL('image/png') // still bad, the size will be larger
$('img').cropper('getCroppedCanvas').toDataURL('image/jpeg') // good, the size will be normal

In short, keep the same image type.

@85351
Hi, i have the same issues. Do you have a good solution for it?

Hi folks. I just overcame the large file issue by passing image/jpeg as mimeType to toBlob method. Uploaded successfully to ASP.Net Core MVC controller.

https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob

cropper.getCroppedCanvas().toBlob(function(blob) {

    var croppedCanvas = Editor.current.cropper.getCroppedCanvas()
    var w = croppedCanvas.getAttribute("width");
    var h = croppedCanvas.getAttribute("height");

    var formData = new FormData();
    formData.append('file', blob);
    formData.append("width", w)
    formData.append("height", h)
    formData.append("profile", "square");

    axios.post('/file/upload', formData).then(function(response) {
        console.log(response.data)
    }).catch(function(err) {
        throw new Error(err)
    })
}, 'image/jpeg');

MVC Controller and ViewModel is here:

        [HttpPost]
        public async Task<IActionResult> Upload(UploadFileViewModel model)
        {
            // TODO @Ahmed hardcoded image dimensions
            var result = await imageUploadHelper.UploadAsync(model.File, model.Profile, 500, 500);

            return Ok(new { model.File.Length, result });
        }
    }

    public class UploadFileViewModel
    {
        public IFormFile File { get; set; }

        public int Width { get; set; }
        public int Height { get; set; }
        public string Profile { get; set; }
    }

@ngaugler @fengyuanchen
Hi, I read that toDataURL generated a rather large PNG file by default. So you suggested to use toDataURL("image/jpeg",0.9") so that the file size will much more reasonable. Now that issue is resolved but it is causing one more issue.
Previously I was using painting a image by white color and then drawing the cropcanvas on top of it and it was working file. As follows:
context.fillStyle = 'white';
context.drawImage(cropcanvas, 0, 0);
But after toDataURL change I am getting black background everytime. Please let me what am i missing here ?

@vikaschauhan17 The getCroppedCanvas method has a fillColor option which you can change the default color:

$().cropper('getCroppedCanvas', {
  fillColor: '#fff',
});

I have a large JPEG image of size 19000 x 23000, and when I crop the image, I just see a blank image instead of the cropped one. I have tried above solutions like including mime type like .toDataURL('image/png') and also limiting the canvas width to 900. But I am still getting blank image after cropping. Any suggestions to solve this?

@icycool60 For a large image, you might need to upload it to a server to crop it as the browser may have a memory limit.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SimonBriche picture SimonBriche  ·  7Comments

naglalakk picture naglalakk  ·  5Comments

Kendokai picture Kendokai  ·  4Comments

jloguercio picture jloguercio  ·  6Comments

richdenis86 picture richdenis86  ·  8Comments