blueimp / jQuery-File-Upload

File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.
https://blueimp.github.io/jQuery-File-Upload/
MIT License
30.94k stars 7.95k forks source link

Jcrop integration #1314

Closed keepdream closed 12 years ago

keepdream commented 12 years ago

Hi can you provide an example of a succesfull Jcrop (or similar) plugin integration ? I looked at the other topics but there wasn't a complete explanation.

Thanks

KD

trofima commented 12 years ago

Hi. To integrate jcrop i used a done callback. This means i add jcrop only after successful upload. In general its look like this: on done you have data object. Data.result is an array of files' information returned from server. For each file (image) you need to create separate cropbox, and initialize jcrop plugin for each cropbox after image was loaded. Than post that data to server and handle croping of image on server side. Well, it's my solution, but there are many another ways.

Code:


var    cropbox_indx = 0, //index of cropbox
        img_data = {}, // keeps uploaded image data
        crop_w = 150,
        crop_h = 125;

$('#fileupload').fileupload({
done: function (e, data) {
        cropbox_indx += 1;
        $.each(data.result, function (index, file) {
            if (data.error == undefined && file.error == undefined){
                // show uploaded images and initialize cropbox
                var id_upload = 'upload'+cropbox_indx,
                    id_img = 'cropbox'+cropbox_indx,
                    img_container = $('<div/>').attr({'id': id_upload, 'class': 'image'}).appendTo($('#files_container')),
                    img = $('<img/>').attr({'id': id_img, 'class': 'cropbox', 'src': file.url, 'style': 'display: none'}).appendTo($('#'+id_upload));
                img.load(function(){
                    img_data[cropbox_indx] = {f_name: file.name, dimensions: {crop_w: crop_w, crop_h: crop_h}, coords: 0};
                    crop(id_img, img, cropbox_indx, img_data); // this function is below
                    img_container.css({'width': img.width, 'height': img.height});
                });
            }
        });
    }
});

// initialize cropbox for each image
function crop(id_img, img, cropbox_indx, img_data){
    var proportion_coef = img_data[cropbox_indx].dimensions.crop_w/img_data[cropbox_indx].dimensions.crop_h,
        cx = img.width()/2,
        cy = img.height()/2;
    if (img.width() >= img.height()){
        var h_crop = img.height()*0.7,
            w_crop = h_crop*proportion_coef,
            x = cx - w_crop/2, //this coords used to set default selection
            y = cy - h_crop/2,
            x2 = x + w_crop,
            y2 = y + h_crop;
    } else {
        w_crop = img.width()*0.7;
        h_crop = w_crop/proportion_coef;
        x = cx - w_crop/2;
        y = cy - h_crop/2;
        x2 = x + w_crop;
        y2 = y + h_crop;
    }
    var coords = {};
    coords = {'x': x, 'y': y, 'w': (x2 - x), 'h': (y2 - y)};
    img_data[cropbox_indx].coords = coords;
    $("#"+id_img).Jcrop({
        onSelect: function(c){
            coords.x = c.x;
            coords.y = c.y;
            coords.w = c.w;
            coords.h = c.h;
            img_data[cropbox_indx].coords = coords;
        },
        aspectRatio: proportion_coef,
        bgOpacity:.2,
        boxWidth: 300,
        boxHeight: 300,
        setSelect: [x, y, x2, y2]
    });
}

after this you need to post data to server:


$('#crop').click(function(){
        var img_data_arr = [];
        $.each(img_data, function(key, value){
            var coords_arr = [],
                dimensions_arr = [];
            $.each(value.coords, function(key, value){
                coords_arr.push('"'+key+'"'+':'+value);
            });
            $.each(value.dimensions, function(key, value){
                dimensions_arr.push('"'+key+'"'+':'+value);
            });
            var id_json = '"'+key+'"'+':{"coords":{'+coords_arr.join(',')+'},"f_name":"'+value.f_name+'","dimensions":{'+dimensions_arr.join(',')+'}}';
            img_data_arr.push(id_json);
        });
        var img_data_json = '{'+img_data_arr.join(',')+'}';
        $.post($('#fileupload').attr('action'), {img_data_json: img_data_json}, function(res){
            var imgs = $.parseJSON(res);
        });
});

and on server i rewrite thumbnail, created by upload handler class


public function crop(){
        $img_data = json_decode(stripslashes($_POST['img_data_json']));
        $imgs = array();
        foreach ($img_data as $key => $value) {
            $img = $this->options['upload_dir'].$value->f_name;
            $thumb = $this->options['image_versions']['thumbnail']['upload_dir'].$value->f_name;
            if (exec('convert')){
                exec("convert $img -crop {$value->coords->w}x{$value->coords->h}+{$value->coords->x}+{$value->coords->y}".
                    " -resize {$value->dimensions->crop_w}x{$value->dimensions->crop_h} $thumb");
            } else {
                $new_img = @imagecreatetruecolor($value->dimensions->crop_w, $value->dimensions->crop_h);
                $src_img = @imagecreatefromjpeg($img);
                list($img_width, $img_height) = @getimagesize($img);
                $write_image = 'imagejpeg';
                @imagecopyresampled(
                    $new_img,
                    $src_img,
                    0, 0, $value->coords->x, $value->coords->y,
                    $value->dimensions->crop_w,
                    $value->dimensions->crop_h,
                    $value->coords->w,
                    $value->coords->h
                ); 
                $write_image($new_img, $thumb);
                 //Free up memory (imagedestroy does not delete files):
                @imagedestroy($src_img);
                @imagedestroy($new_img);
            }
            $image = array(
                'name' => $value->f_name,
                'image_url' => $this->options['upload_url'].$value->f_name,
                'thumbnail_url' => $this->options['image_versions']['thumbnail']['upload_url'].$value->f_name
            );
            $imgs[] = $image;
        }
        echo json_encode($imgs);
    }

You can add jcrop functionality even before uploading, but you'll need to handle scaling of thumbnail and calculating right coords by yourself and you'll have some more troubles.

keepdream commented 12 years ago

Thanks !

reru commented 12 years ago

I'm sorry for reopening a closed issue, but I'm trying to make jcrop work and even if you explained everything very well I still can't make it work. Can you explain it a little bit more? I'm using the template from the example and I added a button called crop. I auto upload the files, so the user can see only the download template. I also change the name of the files. I don't understand where my error is. Thank you very much.

trofima commented 12 years ago

From your question i didn't understand what exactly not working.

trofima commented 12 years ago

But you need to know, that every cropbox must have uniqe id and uniqe initialization of Jcrop. Jcrop for every image (cropbox) must be initialized AFTER the image appear (jquery load event) on the page. Sorry for my English...

reru commented 12 years ago

Thank you very much for your quick answer. Yes, I know that every cropbox must have unique id. So, I want to make something like this: the user loads an image, then he can see the thumb other things and two buttons, one is the delete button and the other one is the crop button. when the user clicks on the crop button I would like to use the jquery-ui crop (with the dialog ) to show the image that he can crop, then the user crops the image clicks on a save or crop button and the image now is the one that he croped, and he can see that image in the list with the other images (the list = the template download from the jquery-file-upload). Sorry for my English too...

reru commented 12 years ago

anyway, in your example, jcrop is initialized after the image appear on done event, no? maybe I'm not passing the right things to the function. what should I have inside my crop button? Maybe I undestood everything wrong... :( It's the first time that I use Jcrop...

trofima commented 12 years ago

Well, when the dialog is opened, you need to append an image (original image, not thumbnail) to some block in dialog. Then on $('your_original_image).load(function(){ ... }) event initialize Jcrop with needed parametrs (for example 'function crop' in second post calles only on img.load(). img was created and appended to some block in row before) . When user choose crop region and press 'ok', send coordinates to server and handle croping on server.

trofima commented 12 years ago

Done calls ones, after image uploaded and in my case it works as i need, but you need to initialize jcrop later, for example, when crop button is clicked or, better, on dialog open event. For example when crop is clicked the appropriate image is appended to dialog (it is hidden on this moment), and on image load event the dialog opens and Jcrop initializes

ouyangkongtong commented 11 years ago

$("#headPhotoId").fileupload({ autoUpload: false, type:type, url: url, acceptFileTypes: /(.|\/)(gif|jpe?g|png)$/i, formData:formData }).on('fileuploadadd', function (e, data){ imgData = data; }).on('fileuploadsend',function(e, data){ data.url +="?x2="+x2+"&w="+w; console.info("fileuploadsend"); });

$("#uploadHeadPhotoBtnId").click(function(){ //$("#headPhotoId").file(); imgData.submit(); });