Image resizing client-side with JavaScript before upload to the server
Asked Answered
C

8

165

I am looking for a way to resize an image client-side with JavaScript (really resize, not just change width and height).
I know it's possible to do it in Flash but I would like to avoid it if possible.

Is there any open source algorithm somewhere on the web?

Coloquintida answered 12/3, 2010 at 17:13 Comment(3)
For those of you that still want to know the answer to this, You Can Do This, but you will need to make some ajax calls for image processing.Stolon
'Need to make'? Well you CAN solve it on the server after all and get that transparently data through AJAX calls. But the whole attempt is to do it client side, and as Jeremy points out that CAN be done. I think this is a great example: github.com/rossturner/HTML5-ImageUploaderHalfprice
With its latest version, Dropzone.js supports client side image resizing before upload.Preclude
S
127

Here's a gist which does this: https://gist.github.com/dcollien/312bce1270a5f511bf4a

(an es6 version, and a .js version which can be included in a script tag)

You can use it as follows:

<input type="file" id="select">
<img id="preview">
<script>
document.getElementById('select').onchange = function(evt) {
    ImageTools.resize(this.files[0], {
        width: 320, // maximum width
        height: 240 // maximum height
    }, function(blob, didItResize) {
        // didItResize will be true if it managed to resize it, otherwise false (and will return the original file as 'blob')
        document.getElementById('preview').src = window.URL.createObjectURL(blob);
        // you can also now upload this blob using an XHR.
    });
};
</script>

It includes a bunch of support detection and polyfills to make sure it works on as many browsers as I could manage.

(it also ignores gif images - in case they're animated)

Soche answered 28/7, 2015 at 7:15 Comment(8)
I feel like you haven't received nearly enough praise for this - I think it works great and is super easy. Thanks for sharing!Monson
Hmm..I had good success with this, but I found that my images (in addition to being resized) also suffered a huge loss of quality (in the form of severe pixelation), though it was outputed at the correct resolution.Automation
hi, I need a snippet to use like this one, and I think this is very good but.. function(blob, didItResize) { // didItResize will be true if it managed to resize it, otherwise false (and will return the original file as 'blob') document.getElementById('preview').src = window.URL.createObjectURL(blob); // you can also now upload this blob using an XHR. here I have a problem.. How can I send this image in a form with just submitting the form. I mean, like a post request triggered by submit button. You know, the usual way. Thank you for the gist!Dorise
@iedmrc99 that would be a new question, but if I'm right, you either use XHR (ajax) or you send the form to be processed Server-Side... If you are processing server side but want images to be resized by the client, then you would need to replace the content of files[] which I believe are not accesible by JS, so you would need to better save the image in a hidden extra field to be sent with the POST, and that would mean sending the same file twice (file[] and the new resized hidden input), unless you avoid the files[] to be sent, putting the input outside the form tag or something like that.Lehr
it is 2017 and the best solution ever I found thanx :DHyperbolism
For anyone having loss of quality (resampling), update the canvas context to set imageSmoothingEnabled as true` and imageSmoothingQuality to high. In typescript that block looks like: const ctx = canvas.getContext('2d'); ctx.imageSmoothingEnabled = true; (ctx as any).imageSmoothingQuality = 'high'; ctx.drawImage(image, 0, 0, width, height);Tenorrhaphy
Is there a chance of making this an npm package? Would be super great!Cogitate
Great solution! Here's a link to an updated typescript version: gist.github.com/dcollien/… thanks to sean-perkins for posting the updated code gist.github.com/sean-perkinsCruickshank
E
62

The answer to this is yes - in HTML 5 you can resize images client-side using the canvas element. You can also take the new data and send it to a server. See this tutorial:

http://hacks.mozilla.org/2011/01/how-to-develop-a-html5-image-uploader/

Egg answered 21/4, 2011 at 15:15 Comment(2)
This is a link-only answer and as such should be a comment.Zarzuela
canvas.mozGetAsFile("foo.png"); is a deprecated functionFlowage
C
12

If you were resizing before uploading I just found out this http://www.plupload.com/

It does all the magic for you in any imaginable method.

Unfortunately HTML5 resize only is supported with Mozilla browser, but you can redirect other browsers to Flash and Silverlight.

I just tried it and it worked with my android!

I was using http://swfupload.org/ in flash, it does the job very well, but the resize size is very small. (cannot remember the limit) and does not go back to html4 when flash is not available.

Curie answered 23/8, 2011 at 11:58 Comment(3)
It's nice to do client-side resizing for when a user tries to upload a 10mb photo that is only going to be stored as a much smaller photo. It'll upload much quicker this way.Antiserum
Same scenario here, as soon as you have a file input and the user is on a smartphone and snaps an image using the camera, its going to be around 10 mb on modern smartphones. If you on the server side anyway are resizing it and only storing a much smaller version of it, you'll save a lot of cellular data and loading time in doing the resizing beforehand in the client.Atreus
Be careful because plupload is AGPL.Atreus
A
11

http://nodeca.github.io/pica/demo/

In modern browser you can use canvas to load/save image data. But you should keep in mind several things if you resize image on the client:

  1. You will have only 8bits per channel (jpeg can have better dynamic range, about 12 bits). If you don't upload professional photos, that should not be a problem.
  2. Be careful about resize algorithm. The most of client side resizers use trivial math, and result is worse than it could be.
  3. You may need to sharpen downscaled image.
  4. If you wish do reuse metadata (exif and other) from original - don't forget to strip color profile info. Because it's applied when you load image to canvas.
Aryn answered 21/4, 2016 at 2:3 Comment(0)
M
6

Perhaps with the canvas tag (though it's not portable). There's a blog about how to rotate an image with canvas here, I suppose if you can rotate it, you can resize it. Maybe it can be a starting point.

See this library also.

Medford answered 12/3, 2010 at 17:38 Comment(1)
Very useful examples. You might want to add a snippet or two to your answer in case these links ever break.Ovariectomy
B
5

You can use a javascript image processing framework for client-side image processing before uploading the image to the server.

Below I used MarvinJ to create a runnable code based on the example in the following page: "Processing images in client-side before uploading it to a server"

Basically I use the method Marvin.scale(...) to resize the image. Then, I upload the image as a blob (using the method image.toBlob()). The server answers back providing a URL of the received image.

/***********************************************
 * GLOBAL VARS
 **********************************************/
var image = new MarvinImage();

/***********************************************
 * FILE CHOOSER AND UPLOAD
 **********************************************/
 $('#fileUpload').change(function (event) {
	form = new FormData();
	form.append('name', event.target.files[0].name);
	
	reader = new FileReader();
	reader.readAsDataURL(event.target.files[0]);
	
	reader.onload = function(){
		image.load(reader.result, imageLoaded);
	};
	
});

function resizeAndSendToServer(){
  $("#divServerResponse").html("uploading...");
	$.ajax({
		method: 'POST',
		url: 'https://www.marvinj.org/backoffice/imageUpload.php',
		data: form,
		enctype: 'multipart/form-data',
		contentType: false,
		processData: false,
		
	   
		success: function (resp) {
       $("#divServerResponse").html("SERVER RESPONSE (NEW IMAGE):<br/><img src='"+resp+"' style='max-width:400px'></img>");
		},
		error: function (data) {
			console.log("error:"+error);
			console.log(data);
		},
		
	});
};

/***********************************************
 * IMAGE MANIPULATION
 **********************************************/
function imageLoaded(){
  Marvin.scale(image.clone(), image, 120);
  form.append("blob", image.toBlob());
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.marvinj.org/releases/marvinj-0.8.js"></script>
<form id="form" action='/backoffice/imageUpload.php' style='margin:auto;' method='post' enctype='multipart/form-data'>
				<input type='file' id='fileUpload' class='upload' name='userfile'/>
</form><br/>
<button type="button" onclick="resizeAndSendToServer()">Resize and Send to Server</button><br/><br/>
<div id="divServerResponse">
</div>
Borneol answered 28/5, 2018 at 20:57 Comment(1)
This looks frickin awesome!Turnspit
D
3

In my experience, this example has been the best solution for uploading a resized picture: https://zocada.com/compress-resize-images-javascript-browser/

It uses the HTML5 Canvas feature.

The code is as 'simple' as this:

compress(e) {
    const fileName = e.target.files[0].name;
    const reader = new FileReader();
    reader.readAsDataURL(e.target.files[0]);
    reader.onload = event => {
        const img = new Image();
        img.src = event.target.result;
        img.onload = () => {
                const elem = document.createElement('canvas');
                const width = Math.min(800, img.width);
                const scaleFactor = width / img.width;
                elem.width = width;
                elem.height = img.height * scaleFactor;

                const ctx = elem.getContext('2d');
                // img.width and img.height will contain the original dimensions
                ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
                ctx.canvas.toBlob((blob) => {
                    const file = new File([blob], fileName, {
                        type: 'image/jpeg',
                        lastModified: Date.now()
                    });
                }, 'image/jpeg', 1);
            },
            reader.onerror = error => console.log(error);
    };
}

There are two downsides with this solution.

The first one is related with the image rotation, due to ignoring EXIF data. I couldn't tackle this issue, and wasn't so important in my use case, but will be glad to hear any feedback.

The second downside is the lack of support foe IE/Edge (not the Chrome based version though), and I won't put any time on that.

Decennary answered 12/3, 2020 at 11:6 Comment(0)
F
2

Yes, with modern browsers this is totally doable. Even doable to the point of uploading the file specifically as a binary file having done any number of canvas alterations.

http://jsfiddle.net/bo40drmv/

(this answer is a improvement of the accepted answer here)

Keeping in mind to catch process the result submission in the PHP with something akin to:

//File destination
$destination = "/folder/cropped_image.png";
//Get uploaded image file it's temporary name
$image_tmp_name = $_FILES["cropped_image"]["tmp_name"][0];
//Move temporary file to final destination
move_uploaded_file($image_tmp_name, $destination);

If one worries about Vitaly's point, you can try some of the cropping and resizing on the working jfiddle.

Friendly answered 12/6, 2016 at 9:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.