繁体   English   中英

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

[英]How should I crop the image at client side using jcrop and upload it?

我正在处理一个组件,其中有文件上传 HTML 控件,使用控件选择图像后,图像将呈现在 HTML5 Canvas 元素上

这是带有示例代码的 JSFiddle: https ://jsfiddle.net/govi20/spmc7ymp/

id=target是 jcrop 图像目标
id=photograph是一个文件上传控件
id=preview是一个画布元素
id=clear_selection是一个可以清除画布的按钮

使用的第三方 JS 库:

<script src="./js/jquery.min.js"></script>
<script src="./js/jquery.Jcrop.js"></script>
<script src="./js/jquery.color.js"></script>

设置 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');
  });

});

清除画布按钮单击事件:

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

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

在 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);     
});

我正在使用以下代码在画布上裁剪和渲染图像。

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

以下是我在上述设置中面临的一系列问题:

  1. updatePreview 函数没有在选择时被调用,因此画布没有被渲染。
  2. 裁剪选择框不可拖动(我正在使用引导 CSS,我怀疑这是由于缺少/不匹配的依赖性)。
  3. Canvas 是HTML5元素,这意味着最终用户必须拥有兼容HTML5浏览器,我正在开发一个拥有数百万用户的应用程序。 强制用户使用最新的浏览器并不是一个可行的选择。 这里的回退机制应该是什么?

这是基本的 html 5 代码:

https://jsfiddle.net/zm7e0jev/

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

您可以通过以下方式在 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);

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

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

请记住,例如,可以滥用增加的帖子大小限制进行 DoS 攻击。

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

https://jsfiddle.net/g3ysk6sf/

然后你可以通过以下方式在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);

更新:

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

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

当我让它工作时,我会发布一个带有后备代码的更新。

更新 2:

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

https://jsfiddle.net/oupxo3pu/

唯一的限制是IE10及以下可以提交的图片大小,如果图片过大js代码会报错。 每个服务器之间为 post 值工作的最大大小是不同的,js 代码有一个变量来设置最大大小。

下面的 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);
}
画布元素还没有后备代码,我正在研究它。
旧浏览器回退中的帖子大小限制是我自己放弃支持旧浏览器的原因之一。

更新 3:

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

http://flashcanvas.net/

它支持裁剪代码所需的所有画布功能。

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

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

http://jsfiddle.net/w1Lh4w2t/

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

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

尽管我们是这样,但最好在最后将数据从画布中取出,并仅在最后将其放入该字段中。

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]);
  }
}

但是,如果你想要更多的功能而不仅仅是裁剪,如果我们将 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();
}

然后在提交时,我们提交任何挂起的操作,如 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();
}

画布被添加到 div 视图中。

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

为了在 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();
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM