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

Posted

技术标签:

【中文标题】JS 客户端 Exif 方向:旋转和镜像 JPEG 图像【英文标题】:JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images 【发布时间】:2014-01-03 05:54:48 【问题描述】:

数码相机照片通常保存为带有 EXIF“方向”标签的 JPEG。为了正确显示,图像需要根据设置的方向进行旋转/镜像,但浏览器会忽略此信息来呈现图像。即使在大型商业网络应用程序中,对 EXIF 方向的支持也可能参差不齐1。同一来源还很好地总结了 JPEG 可以具有的 8 种不同方向

示例图片可在4 获取。

问题是如何在客户端旋转/镜像图像,使其正确显示并在必要时进行进一步处理?

有 JS 库可用于解析 EXIF 数据,包括方向属性2。 Flickr 注意到在解析大图像时可能存在性能问题,需要使用 webworkers 3。

控制台工具可以正确地重新定位图像5。解决问题的 php 脚本可在 6

获得

【问题讨论】:

6和8需要互换 现在几乎所有现代浏览器都支持方向,包括当您调用canvas.drawImage 时,因此您不再需要手动执行此操作。 twitter.com/zcorpan/status/1235709107933503489 【参考方案1】:

github 项目javascript-Load-Image 为 EXIF 方向问题提供了完整的解决方案,可以正确旋转/镜像所有 8 个 exif 方向的图像。见javascript exif orientation的在线演示

图像被绘制到 html5 画布上。它的正确渲染是在js/load-image-orientation.js中通过canvas操作实现的。

希望这可以节省其他人一些时间,并向搜索引擎介绍这个开源 gem :)

【讨论】:

我一直在使用这个库,但最近它在 ios 8.3 上崩溃了,这是我最需要它工作的地方:( 我真的很难看到这有什么帮助。我正在尝试学习它,因此我可以解析页面并在需要旋转图像时旋转图像,或者在实际上传之前检测方向并旋转文件(以某种方式)。该演示也没有。它只是从文件输入中获取一个文件并以正确的方式显示它,这在现实世界中什么时候有用?当我解析我的页面并将图像标签中的 URL 输入到 loadImage 库时,没有 exif 数据,所以不能这样做。对于上传,它返回一个画布对象,所以我不能将它发送到服务器或任何东西。 @igneosaur:您可以使用canvas.toDataURL()将base64编码的数据发送到服务器并解码并保存在服务器端。 与其使用 load-image 项目,不如使用它的一些代码库来烘焙自己的 - 只需查看 github.com/blueimp/JavaScript-Load-Image/blob/master/js/…。 我同意@igneosaur 的观点,在如何使用这个库从 url 中提取 jpeg 图像并正确定位方面非常苦恼。我没有本地文件。【参考方案2】:

Mederr's context transform 完美运行。如果您只需要提取方向,请使用this function - 您不需要任何 EXIF 读取库。下面是在 base64 图像中重新设置方向的功能。 Here's a fiddle for it。我还准备了fiddle with orientation extraction demo。

function resetOrientation(srcBase64, srcOrientation, callback) 
  var img = new Image();    

  img.onload = function() 
    var width = img.width,
        height = img.height,
        canvas = document.createElement('canvas'),
        ctx = canvas.getContext("2d");

    // set proper canvas dimensions before transform & export
    if (4 < srcOrientation && srcOrientation < 9) 
      canvas.width = height;
      canvas.height = width;
     else 
      canvas.width = width;
      canvas.height = height;
    

    // transform context before drawing image
    switch (srcOrientation) 
      case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
      case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
      case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
      case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
      case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
      case 7: ctx.transform(0, -1, -1, 0, height, width); break;
      case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
      default: break;
    

    // draw image
    ctx.drawImage(img, 0, 0);

    // export base64
    callback(canvas.toDataURL());
  ;

  img.src = srcBase64;
;

【讨论】:

不需要switch 方向的默认情况,因为该转换没有任何作用。此外,请考虑使用srcOrientation &gt; 4 &amp;&amp; srcOrientation &lt; 9 而不是[5,6,7,8].indexOf(srcOrientation) &gt; -1,因为它更快且资源密集(RAM 和CPU)更少。那里不需要数组。这在批量处理大量图像时很重要,其中每一位都很重要。否则,很好的答案。点赞! @RyanCasas 我不知道indexOf 与您的提议相比有多大。我用 10M 次迭代运行了一个简单的循环,它的速度提高了 1400%。很好 :D 非常感谢! 我在 android WebView 中使用了这个答案,结果发现,有些 Android 设备在 WebView 中不支持 WebGL(请参阅bugs.chromium.org/p/chromium/issues/detail?id=555116)旋转可能需要很长时间此类设备取决于图像的大小。 当与引用的getOrientation 一起使用时,我很好奇这在性能方面是否有效。 getOrientation 调用fileReader.readAsArrayBuffer,然后我们调用URL.createObjectURL 并将结果传递给resetOrientation,它将这个URL 作为图像加载。这是否意味着图像文件将被浏览器“加载”/读取两次而不是一次,还是我误解了? 另外,对于已经尊重方向的浏览器该怎么办?我相信 iOS Safari 就是这种情况,并且在使用 CSS image-orientation: from-image 的浏览器中也是如此。【参考方案3】:

如果

width = img.width;
height = img.height;
var ctx = canvas.getContext('2d');

然后您可以使用这些转换将图像转换为方向 1

从 方向:

    ctx.transform(1, 0, 0, 1, 0, 0); ctx.transform(-1, 0, 0, 1, width, 0); ctx.transform(-1, 0, 0, -1, width, height); ctx.transform(1, 0, 0, -1, 0, height); ctx.transform(0, 1, 1, 0, 0, 0); ctx.transform(0, 1, -1, 0, height, 0); ctx.transform(0, -1, -1, 0, height, width); ctx.transform(0, -1, 1, 0, 0, width);

在 ctx 上绘制图像之前

【讨论】:

这里到底发生了什么? 有了这个,您只需要知道方向(例如通过 EXIF),然后根据需要旋转。我正在寻找的一半。 这些转换对我不起作用——相反,我使用了 github.com/blueimp/JavaScript-Load-Image/blob/master/js/… 的加载图像项目中的代码来获得我认为经过充分验证的代码,它同时使用了平移、旋转操作画布上下文加上宽度/高度交换。经过数小时的其他尝试转换等实验后,给了我 100% 的结果 为什么在 ctx 上绘制图像很重要?在画布上绘制图像后无法旋转画布? 方向 8 的旋转对我来说是这样的:ctx.transform(0, -1, 1, 0, 0, height);【参考方案4】:

除了@user3096626 回答我认为如果有人提供代码示例会更有帮助,以下示例将向您展示如何修复来自 url(远程图像)的图像方向:


解决方案1:使用javascript(推荐)

    因为 load-image 库不会仅从 url 图片(文件/blob)中提取 exif 标签,我们将同时使用 exif-js@ 987654322@ javascript 库,所以首先将这些库添加到您的页面,如下所示:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.1.0/exif.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-scale.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-orientation.min.js"></script>
    

    注意 exif-js 2.2 版本似乎有问题,所以我们使用了 2.1

    那么基本上我们要做的就是

    a - 使用 window.loadImage() 加载图像

    b - 使用window.EXIF.getData()读取exif标签

    c - 将图像转换为画布并使用window.loadImage.scale()修复图像方向

    d - 将画布放入文档中

给你:)

window.loadImage("/your-image.jpg", function (img) 
  if (img.type === "error") 
    console.log("couldn't load image:", img);
   else 
    window.EXIF.getData(img, function () 
        var orientation = EXIF.getTag(this, "Orientation");
        var canvas = window.loadImage.scale(img, orientation: orientation || 0, canvas: true);
        document.getElementById("container").appendChild(canvas); 
        // or using jquery $("#container").append(canvas);

    );
  
);

当然你也可以从canvas对象中获取base64格式的图片并将它放在img src属性中,所以使用jQuery你可以做到;)

$("#my-image").attr("src",canvas.toDataURL());

这里是完整的代码:github:https://github.com/digital-flowers/loadimage-exif-example


解决方案 2:使用 html(浏览器 hack)

有一个非常快速和简单的 hack,如果图像直接在新标签中打开而没有任何 html,大多数浏览器会以正确的方向显示图像(哈哈,我不知道为什么),所以基本上你可以显示你的通过将 iframe src 属性直接作为图像 url 来使用 iframe 的图像:

<iframe src="/my-image.jpg"></iframe>

解决方案 3:使用 css(只有 ios 上的 firefox 和 safari)

有 css3 属性来修复图像方向,但它仅适用于 firefox 和 safari/ios 的问题仍然值得一提,因为它很快将适用于所有浏览器(来自 caniuse)

img 
   image-orientation: from-image;

【讨论】:

解决方案 1 对我不起作用。它返回的 window.loadImage 是未定义的 如果你没有包含我在步骤 1 中提到的库,就会发生这种情况 我包含了这些库。我喜欢您的示例的简单性,但我不断收到该错误消息。 exif-js 好像坏了,请检查我的编辑我还在 github 上添加了完整代码:github.com/digital-flowers/loadimage-exif-example ok checkout github项目有一个新文件“upload.html”,不客气:)【参考方案5】:

对于那些拥有来自输入控件的文件,不知道它的方向是什么,有点懒惰并且不想包含大型库的人来说,@WunderBart 提供的代码与他的答案融合在一起链接到 (https://***.com/a/32490603) 找到方向。

function getDataUrl(file, callback2) 
        var callback = function (srcOrientation) 
            var reader2 = new FileReader();
            reader2.onload = function (e) 
                var srcBase64 = e.target.result;
                var img = new Image();

                img.onload = function () 
                    var width = img.width,
                        height = img.height,
                        canvas = document.createElement('canvas'),
                        ctx = canvas.getContext("2d");

                    // set proper canvas dimensions before transform & export
                    if (4 < srcOrientation && srcOrientation < 9) 
                        canvas.width = height;
                        canvas.height = width;
                     else 
                        canvas.width = width;
                        canvas.height = height;
                    

                    // transform context before drawing image
                    switch (srcOrientation) 
                        case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
                        case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
                        case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
                        case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
                        case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
                        case 7: ctx.transform(0, -1, -1, 0, height, width); break;
                        case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
                        default: break;
                    

                    // draw image
                    ctx.drawImage(img, 0, 0);

                    // export base64
                    callback2(canvas.toDataURL());
                ;

                img.src = srcBase64;
            

            reader2.readAsDataURL(file);
        

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

这样称呼很容易

getDataUrl(input.files[0], function (imgBase64) 
      vm.user.BioPhoto = imgBase64;
);

【讨论】:

感谢 google 经过 2 多小时的搜索,我找到了正确的解决方案。 为什么有人会懒惰地选择更轻量级的解决方案? Ported to Typescript and optimized 从大约 4 秒到 ~20 毫秒。 Base64 编码是瓶颈 - 使用 image/jpeg 而不是默认的 image/png,并将输出图像的大小调整为最大宽度(在我的情况下为 400 像素)产生了巨大的差异。 (这适用于缩略图,但如果您必须显示完整图像,我建议避免使用 base64 并直接将画布元素直接注入页面......)【参考方案6】:

WunderBart 的回答对我来说是最好的。请注意,如果您的图像通常是正确的,则可以大大加快速度,只需先测试方向,如果不需要旋转,则绕过其余代码。

将来自 wunderbart 的所有信息放在一起,就像这样;

var handleTakePhoto = function () 
    let fileInput: HTMLInputElement = <HTMLInputElement>document.getElementById('photoInput');
    fileInput.addEventListener('change', (e: any) => handleInputUpdated(fileInput, e.target.files));
    fileInput.click();


var handleInputUpdated = function (fileInput: HTMLInputElement, fileList) 
    let file = null;

    if (fileList.length > 0 && fileList[0].type.match(/^image\//)) 
        isLoading(true);
        file = fileList[0];
        getOrientation(file, function (orientation) 
            if (orientation == 1) 
                imageBinary(URL.createObjectURL(file));
                isLoading(false);
            
            else 
            
                resetOrientation(URL.createObjectURL(file), orientation, function (resetBase64Image) 
                    imageBinary(resetBase64Image);
                    isLoading(false);
                );
            
        );
    

    fileInput.removeEventListener('change');



// from http://***.com/a/32490603
export function getOrientation(file, callback) 
    var reader = new FileReader();

    reader.onload = function (event: any) 
        var view = new DataView(event.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.slice(0, 64 * 1024));
;

export function resetOrientation(srcBase64, srcOrientation, callback) 
    var img = new Image();

    img.onload = function () 
        var width = img.width,
            height = img.height,
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext("2d");

        // set proper canvas dimensions before transform & export
        if (4 < srcOrientation && srcOrientation < 9) 
            canvas.width = height;
            canvas.height = width;
         else 
            canvas.width = width;
            canvas.height = height;
        

        // transform context before drawing image
        switch (srcOrientation) 
            case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
            case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
            case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
            case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
            case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
            case 7: ctx.transform(0, -1, -1, 0, height, width); break;
            case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
            default: break;
        

        // draw image
        ctx.drawImage(img, 0, 0);

        // export base64
        callback(canvas.toDataURL());
    ;

    img.src = srcBase64;

【讨论】:

很棒的方法。您还应该考虑处理从getOrientation 返回的-2-1,这样您就不会尝试旋转非jpg 或没有旋转数据的图像。 我进行了更多优化,请参阅我的 comment above - 我刚刚也添加了您的 file.slice() 优化,但真正的提升来自使用更小的图像和image/jpeg 输出格式来创建更小的数据 URL . (即使是 10 兆像素的图像也没有明显的延迟。) 小心file.slice(),因为你会阻止对base64图像源的支持。 感谢标记 - 是否愿意为替代方案提供编辑?【参考方案7】:

一个班轮有人吗?

我还没有看到有人提到browser-image-compression library。它有一个完美的辅助功能。

用法:const orientation = await imageCompression.getExifOrientation(file)

在许多其他方面也是如此有用的工具。

【讨论】:

这会得到方向。但是如何使用这些信息在客户端生成一个新的 .jpg 文件对象呢? 据我所知,您无法创建 File 对象。下一个最好的事情是将图像和方向发送到服务器,并在那里进行文件操作。或者您可以使用canvastoDataURL 方法生成一个base64 字符串。 不,你可以从 Blob 创建一个 File 对象就好了。文件文件(数组部分,字符串文件名,BlobPropertyBag 属性); developer.mozilla.org/de/docs/Web/API/File 金贴纸! @masterxilo 这对我很有用!在过去的两天里,我一直在为方向而苦苦挣扎!由于某种原因,发布的所有转换开关语句都无法处理我手机中的肖像图像,会有黑条。可能是因为我试图同时压缩和旋转。【参考方案8】:

我创建了一个封装在 ES6 模块中的类,它正好解决了这个问题。

它有 103 行,没有依赖关系,并且结构和文档都相当好,易于修改/重用。

处理所有 8 种可能的方向,并且基于 Promise。

给你,希望这仍然对某人有帮助:https://gist.github.com/vdavid/3f9b66b60f52204317a4cc0e77097913

【讨论】:

【参考方案9】:

我正在使用混合解决方案(php+css)。

需要以下容器:

div.imgCont2 容器需要旋转; div.imgCont1 需要缩放的容器 - width:150%; div.imgCont 滚动条需要容器,当图像是 zoomOut 时。

.

<?php
    $image_url = 'your image url.jpg';
    $exif = @exif_read_data($image_url,0,true);
    $orientation = @$exif['IFD0']['Orientation'];
?>

<style>
.imgCont
    width:100%;
    overflow:auto;

.imgCont2[data-orientation="8"]
    transform:rotate(270deg);
    margin:15% 0;

.imgCont2[data-orientation="6"]
    transform:rotate(90deg);
    margin:15% 0;

.imgCont2[data-orientation="3"]
    transform:rotate(180deg);

img
    width:100%;

</style>

<div class="imgCont">
  <div class="imgCont1">
    <div class="imgCont2" data-orientation="<?php echo($orientation) ?>">
      <img src="<?php echo($image_url) ?>">
    </div>
  </div>
</div>

【讨论】:

谢谢,这似乎是最好的解决方案,无论如何满足我的需求。不需要外部库,也不需要长长的不必要代码块。只需使用 php 从 exif 确定方向,将其发送到视图并使用它来触发旋转适当度数的 CSS 类。又好又干净!编辑:这将在实际读取 exif 数据并相应旋转的浏览器中过度旋转图像。 AKA 它将解决桌面上的问题,但会在 ios safari 上创建一个新问题。【参考方案10】:

Wunderbart 的 post 与 statler 的 improvements 一起为我工作。添加更多的 cmets 和语法清理,并传回方向值,我可以随意使用以下代码。只需调用下面的readImageFile() 函数,即可返回转换后的图像和原始方向。

const JpegOrientation = [
    "NOT_JPEG",
    "NORMAL",
    "FLIP-HORIZ",
    "ROT180",
    "FLIP-HORIZ-ROT180",
    "FLIP-HORIZ-ROT270",
    "ROT270",
    "FLIP-HORIZ-ROT90",
    "ROT90"
];


//Provided a image file, determines the orientation of the file based on the EXIF information.
//Calls the "callback" function with an index into the JpegOrientation array. 
//If the image is not a JPEG, returns 0. If  the orientation value cannot be read (corrupted file?) return -1.
function getOrientation(file, callback) 
    
    const reader = new FileReader();
    reader.onload = (e) => 

        const view = new DataView(e.target.result);
        
        if (view.getUint16(0, false) !== 0xFFD8) 
            return callback(0);  //NOT A JPEG FILE
        
        
        const length = view.byteLength;
        let offset = 2;
        while (offset < length) 
            
            if (view.getUint16(offset+2, false) <= 8)   //unknown?
                return callback(-1);
            
            const marker = view.getUint16(offset, false);
            offset += 2;
            if (marker === 0xFFE1) 
                
                if (view.getUint32(offset += 2, false) !== 0x45786966) 
                    return callback(-1); //unknown?
                

                const little = view.getUint16(offset += 6, false) === 0x4949;
                offset += view.getUint32(offset + 4, little);
                const 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));   //found orientation code
                    
                
            
            else if ((marker & 0xFF00) !== 0xFF00) 
                break;
            
            else  
                offset += view.getUint16(offset, false);
            
        
        
        return callback(-1); //unknown?
    ;
    reader.readAsArrayBuffer(file);


//Takes a jpeg image file as base64 and transforms it back to original, providing the
//transformed image in callback.  If the image is not a jpeg or is already in normal orientation,
//just calls the callback directly with the source.
//Set type to the desired output type if transformed, default is image/jpeg for speed.
function resetOrientation(srcBase64, srcOrientation, callback, type = "image/jpeg") 
    
    if (srcOrientation <= 1)   //no transform needed
        callback(srcBase64);
        return;
    
    
    const img = new Image();    

    img.onload = () => 
        const width = img.width;
        const height = img.height;
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext("2d");

        // set proper canvas dimensions before transform & export
        if (4 < srcOrientation && srcOrientation < 9) 
            canvas.width = height;
            canvas.height = width;
         else 
            canvas.width = width;
            canvas.height = height;
        

        // transform context before drawing image
        switch (srcOrientation) 
              
              //case 1: normal, no transform needed
              
              case 2:  
                  ctx.transform(-1, 0, 0, 1, width, 0); 
                  break;
              case 3:
                  ctx.transform(-1, 0, 0, -1, width, height); 
                  break;
              case 4: 
                  ctx.transform(1, 0, 0, -1, 0, height); 
                  break;
              case 5: 
                  ctx.transform(0, 1, 1, 0, 0, 0); 
                  break;
              case 6: 
                  ctx.transform(0, 1, -1, 0, height, 0); 
                  break;
              case 7: 
                  ctx.transform(0, -1, -1, 0, height, width); 
                  break;
              case 8: 
                  ctx.transform(0, -1, 1, 0, 0, width); 
                  break;
              default: 
                  break;
        

        // draw image
        ctx.drawImage(img, 0, 0);

        //export base64
        callback(canvas.toDataURL(type), srcOrientation);
    ;

    img.src = srcBase64;
;


//Read an image file, providing the returned data to callback. If the image is jpeg
//and is transformed according to EXIF info, transform it first.
//The callback function receives the image data and the orientation value (index into JpegOrientation)
export function readImageFile(file, callback) 

    getOrientation(file, (orientation) => 

        console.log("Read file \"" + file.name + "\" with orientation: " + JpegOrientation[orientation]);

        const reader = new FileReader();
        reader.onload = () =>   //when reading complete

            const img = reader.result;
            resetOrientation(img, orientation, callback);
        ;
        reader.readAsDataURL(file);  //start read
        
    );

【讨论】:

【参考方案11】:

除了@fareed namrouti 的回答,

如果必须从文件输入元素浏览图像,则应使用此选项

<input type="file" name="file" id="file-input"><br/>
image after transform: <br/>
<div id="container"></div>

<script>
    document.getElementById('file-input').onchange = function (e) 
        var image = e.target.files[0];
        window.loadImage(image, function (img) 
            if (img.type === "error") 
                console.log("couldn't load image:", img);
             else 
                window.EXIF.getData(image, function () 
                    console.log("load image done!");
                    var orientation = window.EXIF.getTag(this, "Orientation");
                    var canvas = window.loadImage.scale(img,
                        orientation: orientation || 0, canvas: true, maxWidth: 200);
                    document.getElementById("container").appendChild(canvas);
                    // or using jquery $("#container").append(canvas);
                );
            
        );
    ;
</script>

【讨论】:

【参考方案12】:

我编写了一个小 php 脚本来旋转图像。请务必存储图像,以便在每次请求时重新计算它。

<?php

header("Content-type: image/jpeg");
$img = 'IMG URL';

$exif = @exif_read_data($img,0,true);
$orientation = @$exif['IFD0']['Orientation'];
if($orientation == 7 || $orientation == 8) 
    $degrees = 90;
 elseif($orientation == 5 || $orientation == 6) 
    $degrees = 270;
 elseif($orientation == 3 || $orientation == 4) 
    $degrees = 180;
 else 
    $degrees = 0;

$rotate = imagerotate(imagecreatefromjpeg($img), $degrees, 0);
imagejpeg($rotate);
imagedestroy($rotate);

?>

干杯

【讨论】:

【参考方案13】:

就我而言,我需要 exif-auto-rotate 库。

我有来自后端的 base64 格式图像,在使用它之前我必须将其旋转到正确的方向。它会在更改旋转后返回 base64,这对我来说也是一个优点。

这是该库的 npm 链接:https://www.npmjs.com/package/exif-auto-rotate

【讨论】:

【参考方案14】:

这个答案是为 Angular 人准备的,这里有一个包可以帮助你找到方向。

在下面的示例中,我使用 changeEvent 作为输入标签,但如果您有 dataUrl,则可以使用它

如果您没有 dataUrl ,您可以轻松转换 File -> dataUrl 或 blob -> dataUrl。我已附加 File -> 转换器功能,可帮助我将所选图像文件转换为 dataUrl

https://www.npmjs.com/package/ngx-image-compress这是包裹 npm i ngx-image-compress

import  DOC_ORIENTATION, NgxImageCompressService  from 'ngx-image-compress';

@Component(
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss'],
)

export class Test 
     constructor(private imageCompress: NgxImageCompressService) 

      async onFileSelected(event: any) 
         const file: File = event.target.files[0]
         const dataUrl : string = await this.fileToDataURL(file)
         const orientation : DOC_ORIENTATION = await this.imageCompress.getOrientation(file)

      
      
     fileToDataURL(blob: Blob): Promise<string> 
        return new Promise<string>((resolve, reject) => 
           const reader = new FileReader();
           reader.onload = _e => resolve(reader.result as string);
           reader.onerror = _e => reject(reader.error);
           reader.onabort = _e => reject(new Error("Read aborted"));
           reader.readAsDataURL(blob);
    );
  


【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review @LucaKiebel 是的,我很快就会添加代码。

以上是关于JS 客户端 Exif 方向:旋转和镜像 JPEG 图像的主要内容,如果未能解决你的问题,请参考以下文章

js获取图片的EXIF,解决图片旋转问题

PHP read_exif_data和调整方向

JQuery-File-Upload BlueImp文件上传EXIF方向旋转

exif-js 方向在 android 上总是返回 0 但在桌面上工作

exif获取图片旋转角度

Android:如何保存不是(JPEG 和 PNG)的图像文件? [旋转后]