jcrop:实现 EXIF 方向

Posted

技术标签:

【中文标题】jcrop:实现 EXIF 方向【英文标题】:jcrop: implement EXIF Orientation 【发布时间】:2017-03-07 14:24:30 【问题描述】:

我的网站中有一个裁剪工具,用户可以上传图片并对其进行裁剪。

每当我上传手机拍摄的图片时,我都会遇到图像方向错误的情况。

HTML

<div class="modal-body">

            @using (html.BeginForm("Index", "Home", FormMethod.Post, new  id = "formAjax", enctype = "multipart/form-data", action = "/ProfilePicture/Crop" ))
            
                @Html.AntiForgeryToken()

                <label> Upload Photo: </label>
                <label class="btn btn-default btn-file">
                    Browse @Html.TextBoxFor(ModelIndex => ModelIndex.ProfilePic.MyFile, new  id = "file", type = "file", style = "display: none;" )
                </label>

                <br /><br />
                <div id="crop-image-area" style="display:none;">
                    <table border="0" cellpadding="0" cellspacing="5">
                        <tr>
                            <td>
                                <label>Preview: </label>
                                <div class="img-circle" style="width:100px;height:100px;overflow:hidden;margin-left:5px;">
                                    <canvas id="preview"></canvas>
                                </div>
                                <br />
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <label>Crop Here: </label>
                                <img src="" id="cropbox" style="display: none;">
                            </td>
                        </tr>
                    </table>
                    <br />

                    <input type="hidden" id="cropPointX" name="cropPointX" />
                    <input type="hidden" id="cropPointY" name="cropPointY" />
                    <input type="hidden" id="imageCropWidth" name="imageCropWidth" />
                    <input type="hidden" id="imageCropHeight" name="imageCropHeight" />
                    <span>
                        <input type="submit" id="btnCrop" class="btn btn-success" value="Crop" />
                        <label>
                            <img id="loader" src="~/Content/images/loading.gif" style="display:none;" />
                        </label>
                    </span>
                </div>

            
</div>

JS

$(function() 
    var jcrop_api;

    $("#file").change(function() 
        var reader = new FileReader();
        reader.onload = function(e) 
            $('#cropbox').show();
            $('#cropbox').attr('src', e.target.result);
            $('#cropbox').Jcrop(
                onChange: updatePreview,
                onSelect: getcroparea,
                boxWidth: 400,
                boxHeight: 400,
                aspectRatio: 1,
                bgOpacity: .4,
                setSelect: [80, 45, 100, 100]
            , function() 
                //first attempt
                if (jcrop_api != null) 
                    jcrop_api.destroy();
                
                //second attempt - even this does not work
                $(".jcrop-holder").not(":last").remove();
                jcrop_api = this;
            );

            $("#crop-image-area").hide();
            $("#crop-image-area").fadeIn("slow");

        
        reader.readAsDataURL($(this)[0].files[0]);
        if ($('#cropbox').data('Jcrop')) 
            $('#cropbox').data('Jcrop').destroy();
            $('#cropbox').removeAttr('style');
        

    );

    function updatePreview(c) 
        if (parseInt(c.w) > 0) 
            var imageObj = jQuery("#cropbox")[0];
            var canvas = jQuery("#preview")[0];
            var context = canvas.getContext("2d");
            context.beginPath();
            context.arc(50, 50, 50, Math.PI * 4, 0, true);
            context.clip();
            context.closePath();
            context.drawImage(imageObj, c.x, c.y, c.w, c.h, 0, 0, 100, 100);
        
    ;

    function getcroparea(c) 

        imageCropWidth = c.w;
        imageCropHeight = c.h;
        cropPointX = c.x;
        cropPointY = c.y;

        $('#cropPointX').val(Math.round(cropPointX))
        $('#cropPointY').val(Math.round(cropPointY))
        $('#imageCropWidth').val(Math.round(imageCropWidth))
        $('#imageCropHeight').val(Math.round(imageCropHeight))
    

    function destroyCrop() 
        var jcropApi = $('#cropbox').data('Jcrop');
        if (jcropApi !== undefined) 
            jcropApi.destroy();
            $('#cropbox').attr('style', "").attr("src", "");
        
    

    function destroyPreview() 
        $('#preview').removeAttr('src');
    

    function cropImage() 

        $("#loader").show();
        $('#formAjax').ajaxSubmit(
            type: 'POST',
            success: function(result) 
                $("#loader").hide();
                $('#myProfilePicModal').modal('hide');
                $("#crop-image-area").fadeOut("slow");
                destroyCrop();
                destroyPreview();
                $("#alert-success").show();
                $('#newImage').attr('src', 'data:image;base64,' + result.Photo);
                $('.img-avatar').attr('src', 'data:image;base64,' + result.Avatar);
                setTimeout(function() 
                    $("#alert-success").hide();
                , 5000);
            ,
            error: function() 
                $("#loader").hide();
                $('#myProfilePicModal').modal('hide');
                $("#crop-image-area").fadeOut("slow");
                destroyCrop();
                destroyPreview();
                $("#alert-fail").show();
                setTimeout(function() 
                    $("#alert-fail").hide();
                , 5000);
            
        );
    
    $("#btnCrop").on("click", function(e) 
        e.preventDefault();
        cropImage();
    );
);

我不熟悉 EXIF 方向,请帮助我在我的代码中实现它。

【问题讨论】:

【参考方案1】:

我认为您的意思是您想从上传的图片中提取 EXIF 数据,然后旋转图片,使其始终处于某个位置。 对我来说,图像会发生什么情况还不清楚,例如,我用手机上传了一张以横向模式拍摄的照片,那张照片会倒置吗?当我上传使用 portret 模式制作的照片时会发生什么 这张照片会发生什么?因为我不知道这些信息,所以我无法为您创建完整的代码,但我可以帮助您上路。

这是从客户端的图像中提取 EXIF 数据的 sn-p。这将在有人将图像添加到输入文件时触发。此 sn-p 由用户制作:https://***.com/users/893432/ali

function getOrientation(file, callback) 
  var reader = new FileReader();
  reader.onload = function(e) 

    var view = new DataView(e.target.result);
    if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
    var length = view.byteLength, offset = 2;
    while (offset < length) 
      var marker = view.getUint16(offset, false);
      offset += 2;
      if (marker == 0xFFE1) 
        if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
        var little = view.getUint16(offset += 6, false) == 0x4949;
        offset += view.getUint32(offset + 4, little);
        var tags = view.getUint16(offset, little);
        offset += 2;
        for (var i = 0; i < tags; i++)
          if (view.getUint16(offset + (i * 12), little) == 0x0112)
            return callback(view.getUint16(offset + (i * 12) + 8, little));
      
      else if ((marker & 0xFF00) != 0xFF00) break;
      else offset += view.getUint16(offset, false);
    
    return callback(-1);
  ;
  reader.readAsArrayBuffer(file);


// usage:
var input = document.getElementById('input');
input.onchange = function(e) 
  getOrientation(input.files[0], function(orientation) 
    alert('orientation: ' + orientation);
  );
&lt;input id='input' type='file' /&gt;

如果图片包含EXIF数据,这个sn-p会从图片中提取EXIF数据,你可以得到的响应值如下:

-2: not jpeg
-1: not defined

因此,当它不是 jpeg 时,您将得到 -2,而根本没有图片时,您将得到 -1。如果它有效,您将在图像中获得响应之一,并且您必须使用 jcrop 旋转图像。

假设您得到代码 8 并且图像向右旋转 45 度,您可以使用此代码将其向左旋转 45 度,这样您就知道它是直立的。

var jcp = $("#input").Jcrop('api');
jcp.ui.stage.setAngle(45).redraw();

据我所知,此 sn-p 仅适用于 jCrop 2+,您也可以尝试这样做:

$('#cropbox').Jcrop(
        onChange: updatePreview,
        onSelect: getcroparea,
        boxWidth: 400,
        boxHeight: 400,
        setAngle: 45,
        aspectRatio: 1,
        bgOpacity: .4,
        setSelect: [80, 45, 100, 100]
    

但我没有测试过后者。

你最好的选择是:

    使用我提供的 sn-p 提取图像的旋转,根据你得到的数字计算你需要多少旋转图像/翻转图像 然后使用 jCrop 2.0 版重建您的 jCrop 解决方案,并使用我提供的 2 行 sn-p 来旋转您的图像。

这是另一种解决方案,它要求您也稍微更改一下 jCrop 代码以允许旋转。 Make Jcrop tracker not rotate when cropping a rotated image

【讨论】:

嗨,@gertjan 感谢您的回答。抱歉,如果您不清楚我的问题。我想要发生的是,每当用户上传图像时,画布上显示的图像将具有正确的方向。因为到目前为止它输出了错误的方向8(基于上图)。 嗨@Terence,这意味着您必须使用 setAngle 将图像旋转 270 度,或者使用我的回答 jcp.ui.stage.setAngle(45).redraw();

以上是关于jcrop:实现 EXIF 方向的主要内容,如果未能解决你的问题,请参考以下文章

Chrome 图片 EXIF 方向问题

获取 EXIF 方向标签,旋转到正确的方向,处理图像并以正确的方向保存图像

JCrop - 功能未实现

设置 Android 照片 EXIF 方向

JS 客户端 Exif 方向:旋转和镜像 JPEG 图像

EXIF 方向(指南针)标签