我应该如何使用jcrop在客户端裁剪图像并上传它?

时间:2022-08-28 08:13:35

What I am trying to do is select the image using html fileupload control, crop the selection using jcrop, crop image at client side and upload the image. Here is fiddle link https://jsfiddle.net/govi20/spmc7ymp/

我想要做的是使用html fileupload控件选择图像,使用jcrop裁剪选择,在客户端裁剪图像并上传图像。这里是小提琴链接https://jsfiddle.net/govi20/spmc7ymp/

Id's used in code:

target for jcrop image
photograph fileupload control
preview canvas preview
clear_selection button the clear the selected area

Id用于代码:目标为jcrop图像照片fileupload控件预览画布预览clear_selection按钮清除所选区域

setting up the jcrop.

设置jcrop。

<script type="text/javascript">
jQuery(function($){

var api;

$('#target').Jcrop({
  // start off with jcrop-light class
  bgOpacity: 0.5,
  keySupport: false,
  bgColor: 'black',
  minSize:[240,320],
  maxSize:[480,640],
  onChange : updatePreview,
  onSelect : updatePreview, 
  height:160,
  width:120,
  addClass: 'jcrop-normal'
},function(){
  api = this;
  api.setSelect([0,0,240,320]);
  api.setOptions({ bgFade: true });
  api.ui.selection.addClass('jcrop-selection');
  });

});

for clearing the selection.

清除选择。

 jQuery('#clear_selection').click(function(){
  $('#target').Jcrop({    

      setSelect: [0,0,0,0],
    });
});

to display uploaded file in jcrop

在jcrop中显示上传的文件

function readURL(input) {

    if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
            $('#target').attr('src', e.target.result);
            setProperties();       
        }
        reader.readAsDataURL(input.files[0]);
    }
}

 function setProperties(){
   $('#target').Jcrop({         
              setSelect: [0,0,240,320]
        }); 
 }
 $("#photograph").change(function(){
    readURL(this);     
  });

to crop the image and assign it to canvas(got this from one of the threads on *)

裁剪图像并将其分配给画布(从*上的一个线程获取此图像)

function make_base() {
    console.log("make_base called");
    var base_image = new Image();
    base_image.src = '';
    base_image.onload = function () {
        context.drawImage(base_image, 0, 0);
    }
}

var canvas = document.getElementById('preview'),
context = canvas.getContext('2d');

make_base();
function updatePreview(c) {
    console.log("called");
    if(parseInt(c.w) > 0) {
        // Show image preview
        var imageObj = $("#target")[0];
        var canvas = $("#preview")[0];
        var context = canvas.getContext("2d");
        context.drawImage(imageObj, c.x, c.y, c.w, c.h, 0, 0, canvas.width, canvas.height);
    }
};

Issues I am facing:

我面临的问题:

  1. updatePreview function is not getting called on selection, hence no canvas is visible.
  2. updatePreview函数未在选择时调用,因此没有可见的画布。

  3. crop selection is not draggable (I am using bootstrap css).
  4. 裁剪选择不可拖动(我使用bootstrap css)。

  5. Canvas is HTML5 element, that means client should have html5 compatible browser, so how can I remove this condition.
  6. Canvas是HTML5元素,这意味着客户端应该具有html5兼容的浏览器,所以我该如何删除这个条件。

2 个解决方案

#1


15  

Seahorsepip's answer is fantastic. I made a lot of improvements on the non-fallback answer.

Seahorsepip的答案太棒了。我在非后备答案上做了很多改进。

http://jsfiddle.net/w1Lh4w2t/

I would recommend not doing that strange hidden png thing, when an Image object works just as well (so long as we're not supporting fallbacks).

我建议不要做那个奇怪的隐藏的png事情,当Image对象也能正常工作时(只要我们不支持回退)。

var jcrop_api;
var canvas;
var context;
var image;
var prefsize;

Though even then we are, you're better off getting that data out of the canvas at the end and putting it in that field only at the end.

虽然即便如此,你最好还是将这些数据最终从画布中取出,然后只在最后将它放在那个字段中。

function loadImage(input) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();
    reader.onload = function(e) {
      image = new Image();
      image.src = e.target.result;
      validateImage();
    }
    reader.readAsDataURL(input.files[0]);
  }
}

But, if you want more functions than just crop, if we attach the jcrop to an inserted canvas (which we destroy with the jcrop on refresh). We can easily do anything we can do with a canvas, then validateImage() again and have the updated image visible in place.

但是,如果你想要更多的功能而不仅仅是裁剪,如果我们将jcrop附加到插入的画布(我们在刷新时使用jcrop销毁)。我们可以轻松地使用画布执行任何操作,然后再次验证validateImage()并使更新后的图像可见。

function validateImage() {
  if (canvas != null) {
    image = new Image();
    image.src = canvas.toDataURL('image/png');
  }
  if (jcrop_api != null) {
    jcrop_api.destroy();
  }
  $("#views").empty();
  $("#views").append("<canvas id=\"canvas\">");
  canvas = $("#canvas")[0];
  context = canvas.getContext("2d");
  canvas.width = image.width;
  canvas.height = image.height;
  context.drawImage(image, 0, 0);
  $("#canvas").Jcrop({
    onSelect: selectcanvas,
    onRelease: clearcanvas,
    boxWidth: crop_max_width,
    boxHeight: crop_max_height
  }, function() {
    jcrop_api = this;
  });
  clearcanvas();
}

Then on submit we submit any pending operations, like applyCrop() or applyScale(), adding data into hidden fields for fallback stuff, if we have those things needed. We then have a system we can easily just modify the canvas, in any way, then when we submit the canvas data gets sent properly.

然后在提交时,我们提交任何待处理的操作,例如applyCrop()或applyScale(),如果我们需要那些东西,则将数据添加到隐藏字段中以用于后备内容。然后我们有一个系统,我们可以轻松地以任何方式修改画布,然后当我们提交画布时,数据被正确发送。

function applyCrop() {
  canvas.width = prefsize.w;
  canvas.height = prefsize.h;
  context.drawImage(image, prefsize.x, prefsize.y, prefsize.w, prefsize.h, 0, 0, canvas.width, canvas.height);
  validateImage();
}

The canvas is added to a div views.

画布将添加到div视图中。

 <div id="views"></div>

To catch the attached file in PHP (drupal), I used something like:

要在PHP(drupal)中捕获附件,我使用了类似的东西:

    function makeFileManaged() {
        if (!isset($_FILES['croppedfile']))
            return NULL;
        $path = $_FILES['croppedfile']['tmp_name'];
        if (!file_exists($path))
            return NULL;
        $result_filename = $_FILES['croppedfile']['name'];
        $uri = file_unmanaged_move($path, 'private://' . $result_filename, FILE_EXISTS_RENAME);
        if ($uri == FALSE)
            return NULL;
        $file = File::Create([
                    'uri' => $uri,
        ]);
        $file->save();
        return $file->id();
    }

#2


19  

Here's basic html 5 code:

这是基本的html 5代码:

https://jsfiddle.net/zm7e0jev/

This code crops the image, shows a preview and sets the value of an input element to the base64 encoded cropped image.

此代码裁剪图像,显示预览并将输入元素的值设置为base64编码的裁剪图像。

You can fetch the image file in php the following way:

您可以通过以下方式在php中获取图像文件:

//File destination
$destination = "/folder/cropped_image.png";
//Get convertable base64 image string
$image_base64 = $_POST["png"];
$image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
$image_base64 = str_replace(" ", "+", $image_base64);
//Convert base64 string to image data
$image = base64_decode($image_base64);
//Save image to final destination
file_put_contents($destination, $image);

Submitting base64 image string as a post variable has it's server post size limits and base64 encoding makes the cropped image file size even bigger (~33%) then the raw data of the cropped image would be which makes the upload take even longer.

将base64图像字符串作为post变量提交具有服务器发布大小限制和base64编码使得裁剪后的图像文件大小更大(~33%),然后裁剪图像的原始数据将使上载花费更长时间。

To set the post size limit: What is the size limit of a post request?

设置帖子大小限制:帖子请求的大小限制是多少?

Keep in mind that an increased post size limit can be abused for a DoS attack as example.

请记住,例如,DoS攻击可能会滥用增加的邮件大小限制。

Instead I suggest converting the base64 cropped image to a data blob and then add it to the form on submit as a file:

相反,我建议将base64裁剪图像转换为数据blob,然后将其作为文件提交到表单中:

https://jsfiddle.net/g3ysk6sf/

Then you can fetch the image file in php the following way:

然后你可以通过以下方式在php中获取图像文件:

//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);

Update:

FormData() is only partially support in IE10 and not supported in older versions of IE

FormData()仅在IE10中得到部分支持,在旧版本的IE中不受支持

So I suggest sending the base64 string as a fallback, though this will cause problems with bigger images so it needs to check the filesize and show an error popup when the image is above a specific size.

所以我建议发送base64字符串作为后备,但这会导致更大的图像出现问题,因此需要检查文件大小并在图像高于特定大小时显示错误弹出窗口。

I'll post an update with the fallback code below when I've got it working.

当我使用它时,我将使用下面的回退代码发布更新。

Update 2:

I added a fallback for IE10 and below:

我为IE10及以下版本添加了一个后备:

https://jsfiddle.net/oupxo3pu/

The only limitation is the image size that can be submitted when using IE10 and below, in case the image size is too big the js code will throw an error. The maximum size to work for post values is different between each server, the js code has a variable to set the maximum size.

唯一的限制是使用IE10及以下时可以提交的图像大小,如果图像大小太大,js代码将引发错误。每个服务器之间的post值的最大大小是不同的,js代码有一个变量来设置最大大小。

The php code below is adapted to work with above fallback:

下面的PHP代码适用于上述后备:

//File destination
$destination = "/folder/cropped_image.png";
if($_POST["png"]) {//IE10 and below
    //Get convertable base64 image string
    $image_base64 = $_POST["png"];
    $image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
    $image_base64 = str_replace(" ", "+", $image_base64);
    //Convert base64 string to image data
    $image = base64_decode($image_base64);
    //Save image to final destination
    file_put_contents($destination, $image);
} else if($_FILES["cropped_image"]) {//IE11+ and modern browsers
    //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);
}
There is no fallback code for the canvas element yet, I'm looking into it.
The post size limitation in the fallback for older browsers is one of the reasons I dropped support for older browsers myself.

Update 3:

The fallback I recommend for the canvas element in IE8:

我推荐IE8中canvas元素的后备:

http://flashcanvas.net/

It supports all the canvas functions the cropping code needs.

它支持裁剪代码所需的所有画布函数。

Keep in mind it requires flash. There is a canvas fallback (explorercanvas) that does not require flash but it does not support the function toDataURL() which we need to save our cropped image.

请记住,它需要闪光灯。有一个画布后备(explorercanvas)不需要闪存,但它不支持我们需要保存裁剪图像的函数toDataURL()。

#1


15  

Seahorsepip's answer is fantastic. I made a lot of improvements on the non-fallback answer.

Seahorsepip的答案太棒了。我在非后备答案上做了很多改进。

http://jsfiddle.net/w1Lh4w2t/

I would recommend not doing that strange hidden png thing, when an Image object works just as well (so long as we're not supporting fallbacks).

我建议不要做那个奇怪的隐藏的png事情,当Image对象也能正常工作时(只要我们不支持回退)。

var jcrop_api;
var canvas;
var context;
var image;
var prefsize;

Though even then we are, you're better off getting that data out of the canvas at the end and putting it in that field only at the end.

虽然即便如此,你最好还是将这些数据最终从画布中取出,然后只在最后将它放在那个字段中。

function loadImage(input) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();
    reader.onload = function(e) {
      image = new Image();
      image.src = e.target.result;
      validateImage();
    }
    reader.readAsDataURL(input.files[0]);
  }
}

But, if you want more functions than just crop, if we attach the jcrop to an inserted canvas (which we destroy with the jcrop on refresh). We can easily do anything we can do with a canvas, then validateImage() again and have the updated image visible in place.

但是,如果你想要更多的功能而不仅仅是裁剪,如果我们将jcrop附加到插入的画布(我们在刷新时使用jcrop销毁)。我们可以轻松地使用画布执行任何操作,然后再次验证validateImage()并使更新后的图像可见。

function validateImage() {
  if (canvas != null) {
    image = new Image();
    image.src = canvas.toDataURL('image/png');
  }
  if (jcrop_api != null) {
    jcrop_api.destroy();
  }
  $("#views").empty();
  $("#views").append("<canvas id=\"canvas\">");
  canvas = $("#canvas")[0];
  context = canvas.getContext("2d");
  canvas.width = image.width;
  canvas.height = image.height;
  context.drawImage(image, 0, 0);
  $("#canvas").Jcrop({
    onSelect: selectcanvas,
    onRelease: clearcanvas,
    boxWidth: crop_max_width,
    boxHeight: crop_max_height
  }, function() {
    jcrop_api = this;
  });
  clearcanvas();
}

Then on submit we submit any pending operations, like applyCrop() or applyScale(), adding data into hidden fields for fallback stuff, if we have those things needed. We then have a system we can easily just modify the canvas, in any way, then when we submit the canvas data gets sent properly.

然后在提交时,我们提交任何待处理的操作,例如applyCrop()或applyScale(),如果我们需要那些东西,则将数据添加到隐藏字段中以用于后备内容。然后我们有一个系统,我们可以轻松地以任何方式修改画布,然后当我们提交画布时,数据被正确发送。

function applyCrop() {
  canvas.width = prefsize.w;
  canvas.height = prefsize.h;
  context.drawImage(image, prefsize.x, prefsize.y, prefsize.w, prefsize.h, 0, 0, canvas.width, canvas.height);
  validateImage();
}

The canvas is added to a div views.

画布将添加到div视图中。

 <div id="views"></div>

To catch the attached file in PHP (drupal), I used something like:

要在PHP(drupal)中捕获附件,我使用了类似的东西:

    function makeFileManaged() {
        if (!isset($_FILES['croppedfile']))
            return NULL;
        $path = $_FILES['croppedfile']['tmp_name'];
        if (!file_exists($path))
            return NULL;
        $result_filename = $_FILES['croppedfile']['name'];
        $uri = file_unmanaged_move($path, 'private://' . $result_filename, FILE_EXISTS_RENAME);
        if ($uri == FALSE)
            return NULL;
        $file = File::Create([
                    'uri' => $uri,
        ]);
        $file->save();
        return $file->id();
    }

#2


19  

Here's basic html 5 code:

这是基本的html 5代码:

https://jsfiddle.net/zm7e0jev/

This code crops the image, shows a preview and sets the value of an input element to the base64 encoded cropped image.

此代码裁剪图像,显示预览并将输入元素的值设置为base64编码的裁剪图像。

You can fetch the image file in php the following way:

您可以通过以下方式在php中获取图像文件:

//File destination
$destination = "/folder/cropped_image.png";
//Get convertable base64 image string
$image_base64 = $_POST["png"];
$image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
$image_base64 = str_replace(" ", "+", $image_base64);
//Convert base64 string to image data
$image = base64_decode($image_base64);
//Save image to final destination
file_put_contents($destination, $image);

Submitting base64 image string as a post variable has it's server post size limits and base64 encoding makes the cropped image file size even bigger (~33%) then the raw data of the cropped image would be which makes the upload take even longer.

将base64图像字符串作为post变量提交具有服务器发布大小限制和base64编码使得裁剪后的图像文件大小更大(~33%),然后裁剪图像的原始数据将使上载花费更长时间。

To set the post size limit: What is the size limit of a post request?

设置帖子大小限制:帖子请求的大小限制是多少?

Keep in mind that an increased post size limit can be abused for a DoS attack as example.

请记住,例如,DoS攻击可能会滥用增加的邮件大小限制。

Instead I suggest converting the base64 cropped image to a data blob and then add it to the form on submit as a file:

相反,我建议将base64裁剪图像转换为数据blob,然后将其作为文件提交到表单中:

https://jsfiddle.net/g3ysk6sf/

Then you can fetch the image file in php the following way:

然后你可以通过以下方式在php中获取图像文件:

//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);

Update:

FormData() is only partially support in IE10 and not supported in older versions of IE

FormData()仅在IE10中得到部分支持,在旧版本的IE中不受支持

So I suggest sending the base64 string as a fallback, though this will cause problems with bigger images so it needs to check the filesize and show an error popup when the image is above a specific size.

所以我建议发送base64字符串作为后备,但这会导致更大的图像出现问题,因此需要检查文件大小并在图像高于特定大小时显示错误弹出窗口。

I'll post an update with the fallback code below when I've got it working.

当我使用它时,我将使用下面的回退代码发布更新。

Update 2:

I added a fallback for IE10 and below:

我为IE10及以下版本添加了一个后备:

https://jsfiddle.net/oupxo3pu/

The only limitation is the image size that can be submitted when using IE10 and below, in case the image size is too big the js code will throw an error. The maximum size to work for post values is different between each server, the js code has a variable to set the maximum size.

唯一的限制是使用IE10及以下时可以提交的图像大小,如果图像大小太大,js代码将引发错误。每个服务器之间的post值的最大大小是不同的,js代码有一个变量来设置最大大小。

The php code below is adapted to work with above fallback:

下面的PHP代码适用于上述后备:

//File destination
$destination = "/folder/cropped_image.png";
if($_POST["png"]) {//IE10 and below
    //Get convertable base64 image string
    $image_base64 = $_POST["png"];
    $image_base64 = str_replace("data:image/png;base64,", "", $image_base64);
    $image_base64 = str_replace(" ", "+", $image_base64);
    //Convert base64 string to image data
    $image = base64_decode($image_base64);
    //Save image to final destination
    file_put_contents($destination, $image);
} else if($_FILES["cropped_image"]) {//IE11+ and modern browsers
    //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);
}
There is no fallback code for the canvas element yet, I'm looking into it.
The post size limitation in the fallback for older browsers is one of the reasons I dropped support for older browsers myself.

Update 3:

The fallback I recommend for the canvas element in IE8:

我推荐IE8中canvas元素的后备:

http://flashcanvas.net/

It supports all the canvas functions the cropping code needs.

它支持裁剪代码所需的所有画布函数。

Keep in mind it requires flash. There is a canvas fallback (explorercanvas) that does not require flash but it does not support the function toDataURL() which we need to save our cropped image.

请记住,它需要闪光灯。有一个画布后备(explorercanvas)不需要闪存,但它不支持我们需要保存裁剪图像的函数toDataURL()。