9 11 Votes

How to resize Image before Upload in Browser

Tutorial by Stefan Trost | 22/10/2016 at 14:07

In this tutorial I would like to show you how to implement an image upload script in which the image is resized or minimized automatically. However, the special thing about the size reduction is that the resizing is taking place directly and without additional software in the browser of the user on the client-side instead of by means of some server-side PHP script so that the server resources are spared.

We are using HTML 5 technologies for the minimizing such as FileReader and Canvas. In this tutorial, we assume that the user is selecting the image in a file dialog - in the tutorials HTML5 Canvas Upload with Ajax and HTML5 Canvas Upload with an HTML Form, I have described how to send an already existing canvas as an image to the server in order to save it there. This tutorial is based on these other tutorials, but this not necessary to know them for understanding this one.

The HTML-Page

Let us start with the HTML. Fundamentally, we only need an input field for selecting the file and a form for submitting the data at this point.

<input id="inp_file" type="file">

<form method="post" action="">
  <input id="inp_img" name="img" type="hidden" value="">
  <input id="bt_save" type="submit" value="Upload">
</form>

It is important that the input field is intentionally not located within the form. Finally we do not want to send the entire large selected file to the server but only a reduction thereof. We will do this reduction with means of JavaScript later and we are storing the result of the reduction in the hidden field "img" of our form with which the image then can be submitted.

The JavaScript

I want to describe our JavaScript in multiple parts. However, you will find the full HTML page together with the entire code at the end of this tutorial.

document.getElementById('inp_file').addEventListener('change', fileChange, false);

With this line, we are assigning an event listener to our site input field. With each change of the field (that is whenever the user is selecting a new image), the function fileChange() should be called.

Here, we can see the first part of this function:

function fileChange(e) { 
  document.getElementById('inp_img').value = '';
	
  var file = e.target.files[0];

  if (file.type == "image/jpeg" || file.type == "image/png") {
    // ...
  } else {
    document.getElementById('inp_file').value = '';	
    alert('Please only select images in JPG- or PNG-format.');	
  }
}

First, the hidden field "img" (with the ID "inp_img") will be reset. The reason is that the field should be empty independent from the following user action. Otherwise, when the user is chosing his second image and he has selected a wrong file and canceled the selection, the first image would remain in the field. After clearing the field, we are getting an HTML5 File Object using e.target.files[0] that we are saving as the variable file.

Using file.type, we can query some information about which file type the selected file is. Because we only want to allow JPG and PNG images, we are creating an if condition and returning an error message if necessary.

In case that the selected file type is fitting, the code goes on here (the "..." in the example above):

var reader = new FileReader();  
reader.onload = function(readerEvent) {
  // ...
}
reader.readAsDataURL(file);

Now, we know that we have an image of a correct image format so that we can create an HTML5 FileReader-object which is able to read the image data for us. The file can be passed for reading out using the function readAsDataURL(file). Before, we have created a reader event with reader.onload, with which we can define what should happen during reading out.

First, we are creating an image object here, again we are signing on OnLoad-event:

var image = new Image();
image.onload = function(imageEvent) {	
  // ...
}
image.src = readerEvent.target.result;

Within this OnLoad-Event, the actual resizing of the image is taking place:

var max_size = 300;
var w = image.width;
var h = image.height;
			
if (w > h) {  if (w > max_size) { h*=max_size/w; w=max_size; }
} else     {  if (h > max_size) { w*=max_size/h; h=max_size; } }
			
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
canvas.getContext('2d').drawImage(image, 0, 0, w, h);
				
if (file.type == "image/jpeg") {
   var dataURL = canvas.toDataURL("image/jpeg", 1.0);
} else {
   var dataURL = canvas.toDataURL("image/png");	
}
document.getElementById('inp_img').value = dataURL;

Using the variable max_size, we define how large our resulting minimized image should be at the maximum. After that, we are reading out the current width and height of the image and we are storing the values in the variables w and h. Next, depending on our image is of portrait or landscape orientation, we are calculating the new image dimensions proportionately.

As soon as the values are determined, we are creating a canvas object of the desired size in order to draw the image onto it. This is the moment at which the resizing is taking place.

In the next step, we are fetching a base64 encoded string representation of our image using the function .toDataURL which we are writing into our hidden form field for submitting. Because we want to support both, PNG and JPG images, we have built in an appropriate case distinction. By using canvas.toDataURL("image/jpeg", 1.0), we are using the best JPG quality for our image. If you change 1.0 to a lower value, the result will be a higher JPG compression resulting in an image of lesser size. For example, you can use 0.5 for medium quality, 0.1 for low quality or any other value between.

The PHP-Code

The PHP code should receive the form data and it should save the submitted image. Because the code is largely corresponding to the PHP code of the two linked tutorials above, you will find a detailed explanation there and not here.

if (count($_POST) && (strpos($_POST['img'], 'data:image') === 0)) {
	
  $img = $_POST['img'];
  
  if (strpos($img, 'data:image/jpeg;base64,') === 0) {
      $img = str_replace('data:image/jpeg;base64,', '', $img);  
      $ext = '.jpg';
  }
  if (strpos($img, 'data:image/png;base64,') === 0) {
      $img = str_replace('data:image/png;base64,', '', $img); 
      $ext = '.png';
  }
  
  $img = str_replace(' ', '+', $img);
  $data = base64_decode($img);
  $file = 'uploads/img'.date("YmdHis").$ext;
  
  if (file_put_contents($file, $data)) {
      echo "<p>The image was saved as $file.</p>";
  } else {
      echo "<p>The image could not be saved.</p>";
  }	
  
}

The only difference is that we need a case distinction again for caring about the different image formats. Apart from that, in this code, the base64 encoding is reversed and the image is stored on the server in the folder "uploads" under the current date.

The complete File

Especially in the JavaScript part, the explanation was a bit dismantled. Because of that, I want to repeat the full file with the entire HTML, PHP and JavaScript code at this place again.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head><body>

<?php

if (count($_POST) && (strpos($_POST['img'], 'data:image') === 0)) {
	
  $img = $_POST['img'];
  
  if (strpos($img, 'data:image/jpeg;base64,') === 0) {
      $img = str_replace('data:image/jpeg;base64,', '', $img);  
      $ext = '.jpg';
  }
  if (strpos($img, 'data:image/png;base64,') === 0) {
      $img = str_replace('data:image/png;base64,', '', $img); 
      $ext = '.png';
  }
  
  $img = str_replace(' ', '+', $img);
  $data = base64_decode($img);
  $file = 'uploads/img'.date("YmdHis").$ext;
  
  if (file_put_contents($file, $data)) {
      echo "<p>The image was saved as $file.</p>";
  } else {
      echo "<p>The image could not be saved.</p>";
  }	
  
}
					 
?>


<input id="inp_file" type="file">

<form method="post" action="">
  <input id="inp_img" name="img" type="hidden" value="">
  <input id="bt_save" type="submit" value="Upload">
</form>


<script>

  function fileChange(e) { 
     document.getElementById('inp_img').value = '';
	
     var file = e.target.files[0];

     if (file.type == "image/jpeg" || file.type == "image/png") {

        var reader = new FileReader();  
        reader.onload = function(readerEvent) {
  
           var image = new Image();
           image.onload = function(imageEvent) {	
              var max_size = 300;
              var w = image.width;
              var h = image.height;
			
              if (w > h) {  if (w > max_size) { h*=max_size/w; w=max_size; }
              } else     {  if (h > max_size) { w*=max_size/h; h=max_size; } }
			
              var canvas = document.createElement('canvas');
              canvas.width = w;
              canvas.height = h;
              canvas.getContext('2d').drawImage(image, 0, 0, w, h);
				
              if (file.type == "image/jpeg") {
                 var dataURL = canvas.toDataURL("image/jpeg", 1.0);
              } else {
                 var dataURL = canvas.toDataURL("image/png");	
              }
              document.getElementById('inp_img').value = dataURL;	
           }
           image.src = readerEvent.target.result;
        }
        reader.readAsDataURL(file);
     } else {
        document.getElementById('inp_file').value = '';	
        alert('Please only select images in JPG- or PNG-format.');	
     }
  }

  document.getElementById('inp_file').addEventListener('change', fileChange, false);	
		
</script>

</body></html>

You can save this file under the name "resize_form_upload.php", for example.

Upload multiple Images at once

With this code introduced in this tutorial, you can only upload one image at the same time. If you want to allow uploading multiple images at once, you have to adjust the code a bit. I have done this work for you, you can find a solution here.

ReplyPositiveNegativeDateVotes
77 Votes

I don't really know how to thank u for this. Been trying to find such a thing over a week. U made my day.
05/05/2018 at 10:29

ReplyPositive Negative

Stefan Trost

Show Profile | Message
Avatar
35 Votes

If you want to thank me, there is an easy way:

Just donate a bit.

Thank you very much!
05/05/2018 at 10:51

Positive Negative
-11 Vote

The work is good but when I reload the page then the image re-upload once to the server?

How can I stop this error?
08/05/2019 at 03:04

ReplyPositive Negative
00 Votes

That's the normal behaviour when submitting the page again, the same applies for other things like textarea contents. Normally, your browser should warn you in that case.

To prevent that, you have to check on the server side if the same content was uploaded before or transmit a unique id in a hidden field that you can check.
08/05/2019 at 05:02

Positive Negative
00 Votes

This is amazingly helpful to get a hang of how to manage images.

Only issue with it is the orientation problems when uploading from an iphone camera.
22/05/2019 at 17:10

ReplyPositive Negative
00 Votes

I think the iPhone issue is because thr iPhone is storing the image orientation internally. In fact, the original image is always in the same orientation. Only for displaying the image, the orientation is applied. So, you cannot do anything against that.
22/05/2019 at 17:52

Positive Negative
00 Votes

This is an extremely helpful article! Thanks a lot!

By the way, I find it useful to upload all pictures as jpg, as I can then use the jpg quality attribute to reduce the size drastically (var dataURL = canvas.toDataURL("image/jpeg", 0.9); - instead of 1)
19/09/2019 at 16:12

ReplyPositive Negative
Reply

About the Author

Avatar AuthorYou can find Software by Stefan Trost on sttmedia.com. Do you need an individual software solution according to your needs? - sttmedia.com/contact
Show Profile | Message

 

Related Topics

Important Note

Please note: The contributions published on askingbox.com are contributions of users and should not substitute professional advice. They are not verified by independents and do not necessarily reflect the opinion of askingbox.com. Learn more.

Participate

Ask your own question or write your own articles on askingbox.com. How to do.