HTML5 Image uploader with Jcrop

HTML5 Image uploader with Jcrop

126 653455
HTML5 Image uploader with Jcrop
HTML5 Image uploader with Jcrop

HTML5 Image uploader with Jcrop

We have received several inquiries for the last time from our readers with a question – how to upload photos to website. I think that this is an interesting question, and, I decided to lift the veil of this question. But, I think that the basic file upload is a bit boring thing, so, I decided to add an important feature – Cropping. It should be more attractive. Moreover, we are going to use HTML5 FileReader in order to perform cropping with Jcrop (jquery library) at client size. That will get rid of unnecessary steps. In the result – we should get 3-step process: select file -> crop -> upload. During selecting a file, we will check for the file type and size (in order to avoid huge files). Finally, when everything is ready and we have uploaded the cropped image – we will accept (upload) this file into our website (into certain folder). Please pay attention, that GD library is required to process images. If you are ready – let’s start.

It is the very time to test our demo and download the sources:

Live Demo


download in package


Step 1. HTML

Our first step is html markup. first, we have to put styles and scripts in the HEAD section:

<!-- add styles -->
<link href="css/main.css" rel="stylesheet" type="text/css" />
<link href="css/jquery.Jcrop.min.css" rel="stylesheet" type="text/css" />
<!-- add scripts -->
<script src="js/jquery.min.js"></script>
<script src="js/jquery.Jcrop.min.js"></script>
<script src="js/script.js"></script>

And now, in the BODY section we can put our form:

<div class="bbody">
    <!-- upload form -->
    <form id="upload_form" enctype="multipart/form-data" method="post" action="upload.php" onsubmit="return checkForm()">
        <!-- hidden crop params -->
        <input type="hidden" id="x1" name="x1" />
        <input type="hidden" id="y1" name="y1" />
        <input type="hidden" id="x2" name="x2" />
        <input type="hidden" id="y2" name="y2" />
        <h2>Step1: Please select image file</h2>
        <div><input type="file" name="image_file" id="image_file" onchange="fileSelectHandler()" /></div>
        <div class="error"></div>
        <div class="step2">
            <h2>Step2: Please select a crop region</h2>
            <img id="preview" />
            <div class="info">
                <label>File size</label> <input type="text" id="filesize" name="filesize" />
                <label>Type</label> <input type="text" id="filetype" name="filetype" />
                <label>Image dimension</label> <input type="text" id="filedim" name="filedim" />
                <label>W</label> <input type="text" id="w" name="w" />
                <label>H</label> <input type="text" id="h" name="h" />
            <input type="submit" value="Upload" />

I hope that all is clear at this step – this is usual upload form, with hidden and visible fields, once we have selected an image, we will see second step (crop). Once we have cropped necessary area, we can Upload our result.

Step 2. CSS

Now, I would like to give you CSS styles to stylize our form:


.bheader {
    background-color: #DDDDDD;
    border-radius: 10px 10px 0 0;
    padding: 10px 0;
    text-align: center;
.bbody {
    color: #000;
    overflow: hidden;
    padding-bottom: 20px;
    text-align: center;
    background: -moz-linear-gradient(#ffffff, #f2f2f2);
    background: -ms-linear-gradient(#ffffff, #f2f2f2);
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f2f2f2));
    background: -webkit-linear-gradient(#ffffff, #f2f2f2);
    background: -o-linear-gradient(#ffffff, #f2f2f2);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2');
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2')";
    background: linear-gradient(#ffffff, #f2f2f2);
.bbody h2, .info, .error {
    margin: 10px 0;
.step2, .error {
    display: none;
.error {
    font-size: 18px;
    font-weight: bold;
    color: red;
.info {
    font-size: 14px;
label {
    margin: 0 5px;
input {
    border: 1px solid #CCCCCC;
    border-radius: 10px;
    padding: 4px 8px;
    text-align: center;
    width: 70px;
.jcrop-holder {
    display: inline-block;
input[type=submit] {
    background: #e3e3e3;
    border: 1px solid #bbb;
    border-radius: 3px;
    -webkit-box-shadow: inset 0 0 1px 1px #f6f6f6;
    box-shadow: inset 0 0 1px 1px #f6f6f6;
    color: #333;
    font: bold 12px/1 "helvetica neue", helvetica, arial, sans-serif;
    padding: 8px 0 9px;
    text-align: center;
    text-shadow: 0 1px 0 #fff;
    width: 150px;
input[type=submit]:hover {
    background: #d9d9d9;
    -webkit-box-shadow: inset 0 0 1px 1px #eaeaea;
    box-shadow: inset 0 0 1px 1px #eaeaea;
    color: #222;
    cursor: pointer;
input[type=submit]:active {
    background: #d0d0d0;
    -webkit-box-shadow: inset 0 0 1px 1px #e3e3e3;
    box-shadow: inset 0 0 1px 1px #e3e3e3;
    color: #000;

Step 3. JS

Our next step – is javascript. Please review the result code (my comments are below the code):


// convert bytes into friendly format
function bytesToSize(bytes) {
    var sizes = ['Bytes', 'KB', 'MB'];
    if (bytes == 0) return 'n/a';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
// check for selected crop region
function checkForm() {
    if (parseInt($('#w').val())) return true;
    $('.error').html('Please select a crop region and then press Upload').show();
    return false;
// update info by cropping (onChange and onSelect events handler)
function updateInfo(e) {
// clear info by cropping (onRelease event handler)
function clearInfo() {
    $('.info #w').val('');
    $('.info #h').val('');
// Create variables (in this scope) to hold the Jcrop API and image size
var jcrop_api, boundx, boundy;
function fileSelectHandler() {
    // get selected file
    var oFile = $('#image_file')[0].files[0];
    // hide all errors
    // check for image type (jpg and png are allowed)
    var rFilter = /^(image\/jpeg|image\/png)$/i;
    if (! rFilter.test(oFile.type)) {
        $('.error').html('Please select a valid image file (jpg and png are allowed)').show();
    // check for file size
    if (oFile.size > 250 * 1024) {
        $('.error').html('You have selected too big file, please select a one smaller image file').show();
    // preview element
    var oImage = document.getElementById('preview');
    // prepare HTML5 FileReader
    var oReader = new FileReader();
        oReader.onload = function(e) {
        // contains the DataURL which we can use as a source of the image
        oImage.src =;
        oImage.onload = function () { // onload event handler
            // display step 2
            // display some basic image info
            var sResultFileSize = bytesToSize(oFile.size);
            $('#filedim').val(oImage.naturalWidth + ' x ' + oImage.naturalHeight);
            // destroy Jcrop if it is existed
            if (typeof jcrop_api != 'undefined') {
                jcrop_api = null;
                // initialize Jcrop
                    minSize: [32, 32], // min crop size
                    aspectRatio : 1, // keep aspect ratio 1:1
                    bgFade: true, // use fade effect
                    bgOpacity: .3, // fade opacity
                    onChange: updateInfo,
                    onSelect: updateInfo,
                    onRelease: clearInfo
                }, function(){
                    // use the Jcrop API to get the real image size
                    var bounds = this.getBounds();
                    boundx = bounds[0];
                    boundy = bounds[1];
                    // Store the Jcrop API in the jcrop_api variable
                    jcrop_api = this;
    // read selected file as DataURL

There are several common functions in the beginning: bytesToSize, checkForm, updateInfo and clearInfo. They are pretty easy. The next function (fileSelectHandler) is more complex, basically, this is the main function. When we have selected a file (I suppose – image file), we will check this file for Type and Size. You can see here a filter for image formats: png and jpg. Plus, we don’t need very large images, I think that 250kb is more than enough. Then, if everything is ok, we can read our selected file using FileReader::readAsDataURL (html5 function). And, once it has loaded, we can continue: we should display step2 with Preview and info section, and then – we have to initialize (or – reinitialize) Jcrop for our Preview image. This is how it works. Once we have cropped the image, we can click ‘Upload’ button in order to send result to the server.

Step 4. PHP

In this step – we have to accept (and upload) our result photo. I prepared next useful PHP function for you:


function uploadImageFile() { // Note: GD library is required for this function
    if ($_SERVER['REQUEST_METHOD'] == 'POST') {
        $iWidth = $iHeight = 200; // desired image result dimensions
        $iJpgQuality = 90;
        if ($_FILES) {
            // if no errors and size less than 250kb
            if (! $_FILES['image_file']['error'] && $_FILES['image_file']['size'] < 250 * 1024) {
                if (is_uploaded_file($_FILES['image_file']['tmp_name'])) {
                    // new unique filename
                    $sTempFileName = 'cache/' . md5(time().rand());
                    // move uploaded file into cache folder
                    move_uploaded_file($_FILES['image_file']['tmp_name'], $sTempFileName);
                    // change file permission to 644
                    @chmod($sTempFileName, 0644);
                    if (file_exists($sTempFileName) && filesize($sTempFileName) > 0) {
                        $aSize = getimagesize($sTempFileName); // try to obtain image info
                        if (!$aSize) {
                        // check for image type
                        switch($aSize[2]) {
                            case IMAGETYPE_JPEG:
                                $sExt = '.jpg';
                                // create a new image from file
                                $vImg = @imagecreatefromjpeg($sTempFileName);
                            case IMAGETYPE_PNG:
                                $sExt = '.png';
                                // create a new image from file
                                $vImg = @imagecreatefrompng($sTempFileName);
                        // create a new true color image
                        $vDstImg = @imagecreatetruecolor( $iWidth, $iHeight );
                        // copy and resize part of an image with resampling
                        imagecopyresampled($vDstImg, $vImg, 0, 0, (int)$_POST['x1'], (int)$_POST['y1'], $iWidth, $iHeight, (int)$_POST['w'], (int)$_POST['h']);
                        // define a result image filename
                        $sResultFileName = $sTempFileName . $sExt;
                        // output image to file
                        imagejpeg($vDstImg, $sResultFileName, $iJpgQuality);
                        return $sResultFileName;
$sImage = uploadImageFile();
echo '<img src="'.$sImage.'" />';

As you see – we have to check for image size and format at the server’s side too. In the result – we will get double protection (at user side and server side) from unwanted files. Once we have uploaded the image (using move_uploaded_file) – we can crop it (using GD’s functions: imagecreatefromjpeg, imagecreatetruecolor and imagecopyresampled), and – turn result into image file using ‘imagejpeg’ function. Please pay attention – that in the result we will get a small image (which is onle 200×200), so, beside cropping, we also resize the image. I selected next desired size for all incoming photos: 200×200 (this is a good format for .. profile’s avatars as example). Finally – we can display this image on the screen. That’s all.

Live Demo


We have just created own HTML5 Image uploader with Jcrop. I hope that you like it. It would be nice of you to share our materials with your friends. Good luck and welcome back!


Understanding Closures

0 19730


    • Hello Ian,
      I haven’t tested this in Safary (because we don’t have it installed here), but, I tested it in FF and Chrome (I think that Chrome and Safari have a very similar core). Are you sure that it doesn’t work in Safari? Our own demo page or your local version?

  1. Hi There,

    Script works beautifully, except in Internet Explorer (IE8 and IE9). I’m not sure what’s causing the problem but the image is not displayed after selecting it. Does it work in Internet Explorer for you, or is this a general problem?

    I’d like to use this on a live site, but obviously a no go unless I can get it working in IE.

    Any info appreciated,



    • Hello Mick,
      Yes, you are right, a lot of html5-based function don’t work in IE, even for file upload (as example – FileReader)

    • Hello Don,
      As I mentioned before – IE browser doesn’t support several important javascript functions

  2. Nice tutorial. Can you suggest how to add code for viewing preview pane of cropped region of selected image.

  3. How can I change it to accept the pre-uploaded image size from 250k to 4M – I have tried changing the 250 to 4000 in the PHP and the JS code but it still restricted?

  4. My mistake! It was working, but with a very large image only a part of it is on the screen for cropping tool. How can I restrict the maximum display size for the original image?

    • Hello Ray,
      Please look our code below line 67, as you see – we can use oImage.naturalWidth and oImage.naturalHeight variables. It will help you to ignore very huge images.

  5. Hello Seb,
    Yes, you can, but you will need to check functionality to work with server’s files.

  6. Hi, this is an awesome plugin, first of all! So thank you for putting this online! It’s been a huge help!

    I’m having issues with the same thing that Ray was mentioning. If a user uploads an image that uses a large set of dimensions like 1500 x 600… or 600 x 1500… Is there any way to specify that the display image is never any bigger than 500 x 500. When I’ve adjusted this in the CSS, the cropper seems to get confused.

    You answered Rays question, but I don’t really understand how you would adjust the oImage.naturalWidth and oImage.naturalHeight variables in order to achieve this result.

    Thanks so much!!

  7. Hello Lauren,
    try to use extra css style like:
    #preview, .jcrop-holder {

      • Thanks, Andrew – awesome feature! Using jQuery 1.9.1, the user is unable to use the cropping function. The image preview appears, but no cropping functionality. This is fixed by switching jQuery 1.9.1 with the 1.7.2 jQuery library included in the package.

  8. Do you see any JS error when you use jQuery 1.9.1? I’ve just tried it with jQuery v1.9.1, and, it seems that it works fine

  9. Something that bothers me is when you have uploaded a picture and then upload a new one, only the data and information changes, not the ‘jcrop-holder > img’..

    I have tried to add a class in the Jcrop plugin so that the img-tag in ‘jcrop-holder’ can be called through your script and then change the src from there, but it still doesn’t seem to work. Do you have any ideas about how to work this out?

    • Hello Philip,
      Yes, this is very interesting question, but, I’ve just solved it, there are only two changes:
      1. please move var jcrop_api, boundx, boundy; to the top (before function fileSelectHandler)
      2. and, we have to wrap $(‘#preview’).Jcrop initialization with setTimeout. Because it takes a few time to initialize image with new source. My result is:
      // initialize Jcrop

  10. Hello,
    Excuse me, I have doubt in this moment.
    My cuestion is this code can use conection in the database?
    The image can save in the database? because when run the code this upload the image but the image can´t save and automatically disappears.

    Can you help me for this cuestion?


    • Hello Blanca,
      This lesson is not supposed to save uploaded files and make records in database. If you need it – you always can add an extra code to save files and save necessary info into database. The main purpose of this lesson was to show how to work with JCrop and operate with cropped image using GD image library. You can add your custom code to operate with files and database in upload.php file

  11. Hi,
    Great script but I experiment an issue : I changed the max upload file to 4Mb, this looked great but when I try to upload large image (I tried with a 987ko image), the image is always cropped from the top left corner. It is ok with smaller images.
    Any idea ?

    • One more thing: a 1Mo png will work fine but a 1Mo jpg will not. It looks like a large jpg problem only.

  12. I founded the issue : my preview is displayed in a responsive template so my measure are absolute in a resized image. I will try to improve that by adding a ratio to apply in case the image is resized.

  13. Hi,

    I found a solution to use your script when the preview resized the image.

    In the js, I added line 90:
    var dImageWidth = document.getElementById(‘preview’).width;
    var dImageHeight = document.getElementById(‘preview’).height;

    var dratio = oImage.naturalWidth/dImageWidth;

    This is to calculate the ratio between the displaeyed resized image and the true size of the image.

    In the html, I added within the visible inputs:
    Ratio d’affichage
    Largeur affichée
    Hauteur affichée

    This is to display and post the ratio.

    In the php I export the image using the ratio, so I replaced the line starting with : imagecopyresampled($vDstImg, $vImg, 0, 0…


    imagecopyresampled($vDstImg, $vImg, 0, 0, (int)$x1, (int)$y1, $iWidth, $iHeight, (int)$w, (int)$h);

    And voilà, it is now possible to use your script even if the preview show a 3000px width image in 1000px.

    • Hello Thomas,
      Sorry for my late answer, as usual, I answer my readers periodically, but, due of amount of messages, there can be delays.
      Firstly, thank you for your valuable response, I think that it should help to other our readers. Your changes look correct, congratulation!

      • Thanks ever so much for your Bootstrap workaround Thomas, but it won’t work with an iOS device due to the .width and .height. This does work, however:

        var dImageWidth = oImage.clientWidth;
        var dImageHeight = oImage.clientHeight;
        var dratio = oImage.naturalWidth/dImageWidth;

        Moving on… HTML5 Image uploader will not work with Safari <6 due to a lack of support for FileReader().

  14. Thank you for your script
    How do you do if you want to change the picture uploaded and upload a new one ?

  15. PS: Thanks for posting this article in the first place, Andrew. Despite a few issues with Bootstrap and Safari, it is still a great piece of work for a mobile solution, which saved me lots of time :)

    For those who want a ‘loader’ image, try this:

    1. Place this within the .step2 div: uploading

    2. Place this within your css file (the padding helps mobile users scroll):
    .step2 {
    padding:0 3%;
    #loader {
    background: rgb(0,0,0) url(../img/loader.gif) no-repeat center 20px;
    background: rgba(0,0,0,0.6) url(../img/loader.gif) no-repeat center 20px;
    background-size: 50% 50%;
    margin:40px 3%;

  16. continued from above (submitted too soon in error)…

    3. Place this within fileSelectHandler() in the JS file (assuming you’re also using the bootstrap mod):
    // prepare the position of the the loader gif
    document.getElementById(‘loader’).style.left = ((dImageWidth/2)-75) + ‘px’;
    document.getElementById(‘loader’) = ((dImageHeight/2)-75) + ‘px’;

    4. Place this within the JS file
    // show a progress wheel (if crop region selected)
    function loader() {
    if (parseInt($(‘#w’).val()))
    document.getElementById(‘loader’).style.display = “block”;

    5. Change your submit button as follows:

    6. Upload a ‘loader’ wheel graphic to /img.

  17. OK, I cannot amend my posts above, and I’ve noticed that my HTML has not been escaped so I need to add the following notes for my two posts above:

    Step 1 above is a div with an ID of ‘loader’ containing text of ‘uploading’.

    Step 5 is the normal ‘upload’ button but with an onclick event of “loader()”.

  18. Hello!
    Im curious, if there is some way, how to make the floating image “non-floating”. Now when I select a file, the image will display over the rest of the page – in my case it looks horrible :) What I would want is: After selecting the image from the harddrive, the image shows but everything on the page, what is under it, will be moved down according to the picture size… Can this be done? Thanks

    • Hi Michal,
      Your question is unclear, .. because after we have selected our image, all other content is always below this image (it is not on the left or on the right, but – below)

  19. This is an awesome tutorial. Online image cropper is soo important! Thanks man. Also, thanks to Thomas for the additional big image script lines.

  20. Hello, great tutorial! Works fine for me, but if I select a picture and then I do it again, the picture does not change. Why is that? Thank you!

  21. Hi,

    Thanks for this great tutorial, it’s very nice and useful

    I noticed that once you selected a file and the “.step2” div fades in with its preview, any further use of the “choose file” button is ignored by the system.
    I would like to give to the user the possibility to select a new file in case he is not happy with the crop preview of the currently selected image.
    I tried to modify the script to achieve this feature, but so far I have been unsuccessful.
    The only thing I was able to achieve is to replace the button with its clone and erase it:
    var control = $(“#image_file”);
    This allows me to select a new file… however the preview doesn’t change.
    I already tried the following solutions, to no avail:

    1) var image = $(‘#preview’);

    2) document.getElementById(‘preview’).src=””;

    Can you please help me?

    Thank you

    • Hello Umberto,
      This is strange, because I don’t understand your changes. To tell the truth, if you are unhappy with your preview, you can easily click on the same ‘choose file’ button to select another image to crop. And it will work, because of:
      It always calls this function when we choose any new image.

  22. Hi, very useful script.
    Effectively not working with ie8/ie9 and safari.
    Somebody found a issue or know whichare the javascript functions who doesnt’ work ?

  23. Hi.
    I am trying to implement the Image Crop and Upload function in Java. The JQuery version I’m using in 1.7.3.
    Can you provide some inputs on how can I get the cropped image object in JavaScript?

  24. Thanks, awesome script!

    I’m having some trouble when I select a image and then change it to another image. The old image is replaced when I use the timeout function as Santiago stated above, but the new image gets the size and proportions from the first image. Any ideas how to solve this?

    I tried using some jQuery to resize the new image, but I then ran into problems with the cropping area going outside of the image area. I also had some problems with outdated jquery functions for browser check but I found a newer version of jcrop and used it together with this script and my newer jQuery version. Maybe there is a conflict there.

    Is there a simple fix to add a remove image button if you select one but change your mind?
    If anyone has an updated working version of the whole script with the changes from this thread it would be awesome.

    Thanks again!

    • Hi Sebastian,
      Basically, you can just select another image using the same ‘choose image’ button. Doesn’t it work for you? I can offer you next solution – try to call the initialization of JCrop after ~3 seconds, example:
         // initialize Jcrop
            minSize: [32, 32], // min crop size
            aspectRatio : 1, // keep aspect ratio 1:1
            bgFade: true, // use fade effect
            bgOpacity: .3, // fade opacity
            onChange: updateInfo,
            onSelect: updateInfo,
            onRelease: clearInfo
         }, function(){

            // use the Jcrop API to get the real image size
            var bounds = this.getBounds();
            boundx = bounds[0];
            boundy = bounds[1];

            // Store the Jcrop API in the jcrop_api variable
            jcrop_api = this;

      • I have the same problem. The cropping preview updates, but not the main image. The first time through, jcrop initializes and runs fine. The second time jcrop is initialized, jcrop’s callback function isn’t called, regardless of whether we use settimeout to delay it or not. Thus far, I haven’t worked out why.

  25. Hi!

    Thank you! Unfortunately it’s still not working. I’ve using the code posted on the top of the page.
    It’s not working in the live demo either.

    When I choose a new image it gets the dimensions from the previous one.
    I added a little (unpretty) code right after the jcrop init and it fixes the dimension problem, as long as the timeout function is not there.
    It seems like if I fix the dimension problem, the cropping isn’t working and vice versa. There is probably a simple solution to this. Please help.


    var dImageWidth = document.getElementById('preview').width;
    var dImageHeight = document.getElementById('preview').height;
    var preview_width = $('.jcrop-holder').width();
    var dratio = dImageWidth/preview_width;
    $('#preview, .jcrop-holder, .jcrop-holder img, .image-crop').css('width',preview_width);
    boundx = preview_width;

  26. Hi Sebastian,
    In order to fix it, I added three new lines of code below the jcrop_api.destroy() :

    jcrop_api = null;

  27. Ha! I solved it. I took away all my ugly code and the 3000 timeout and added this. Now it works perfect!!

    [code]// preview element
    $(‘#preview’).css(‘width’, ‘auto’).css(‘height’, ‘auto’);[/code]

  28. Sir , i want to make it workable in internet explore , even image is not coming in internet explore , how can i do this ,please guide

  29. hi, this is amazing. thank you. but i cant change the aspect ratio whenever i change it to 16/9 or other values, it still remains 1. any help?

    • Hello Ruthie,
      You can change the aspect ratio in ‘aspectRatio’ param (try to substitute the default ‘1’ with 16/9)

  30. Fantastic script Andrew, thanks for taking the time to share!!! I successfully implemented your fix for the issue with refreshing the preview image when selecting a different image, thanks for that.

    I have one issue that I hope you can help with — I just want to perform a check on the image dimensions before adding to preview because if the dimension of an image are too large, then the upload button will go outside of clickable region and user cannot upload. Also only a portion of the image is available to crop.

    Is there a simple way to set maxwidth and maxheight and checking selected image before adding to preview? I would like to display an error similar to the current filesize check.

    • Hi Trevor,
      To tell the truth, there is only one way: to load this image, and then you will be able to use Width and Height of the image. Example:
      var img = new Image();
      img.onload = function() {
         alert(this.width + ‘x’ + this.height);
      img.src = ‘your remote image’;

Leave a Reply