我想根据相机在 JPEG EXIF 图像数据中设置的原始旋转来旋转照片。诀窍是所有这些都应该在浏览器中使用 JavaScript 和<canvas>.
JavaScript 如何访问 JPEG、本地文件 API 对象、本地<img>或远程<img>、EXIF 数据以读取旋转信息?
服务器端的回答不行;我正在寻找客户端解决方案。
我想根据相机在 JPEG EXIF 图像数据中设置的原始旋转来旋转照片。诀窍是所有这些都应该在浏览器中使用 JavaScript 和<canvas>.
JavaScript 如何访问 JPEG、本地文件 API 对象、本地<img>或远程<img>、EXIF 数据以读取旋转信息?
服务器端的回答不行;我正在寻找客户端解决方案。
如果你只想要方向标签而不想要其他任何东西并且不喜欢包含另一个巨大的 javascript 库,我写了一个小代码来尽可能快地提取方向标签(它使用 DataView 并且readAsArrayBuffer在 IE10+ 中可用,但你可以编写您自己的旧浏览器数据阅读器):
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) 
        {
            if (view.getUint16(offset+2, false) <= 8) return callback(-1);
            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);
    });
}
<input id='input' type='file' />
value观:
-2: not jpeg
-1: not defined
对于那些使用 Typescript 的人,您可以使用以下代码:
export const getOrientation = (file: File, callback: Function) => {
  var reader = new FileReader();
  reader.onload = (event: ProgressEvent) => {
    if (! event.target) {
      return;
    }
    const file = event.target as FileReader;
    const view = new DataView(file.result as ArrayBuffer);
    if (view.getUint16(0, false) != 0xFFD8) {
        return callback(-2);
    }
    const length = view.byteLength
    let offset = 2;
    while (offset < length)
    {
        if (view.getUint16(offset+2, false) <= 8) return callback(-1);
        let marker = view.getUint16(offset, false);
        offset += 2;
        if (marker == 0xFFE1) {
          if (view.getUint32(offset += 2, false) != 0x45786966) {
            return callback(-1);
          }
          let little = view.getUint16(offset += 6, false) == 0x4949;
          offset += view.getUint32(offset + 4, little);
          let tags = view.getUint16(offset, little);
          offset += 2;
          for (let 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);
}
您可以将exif-js库与 HTML5 文件 API 结合使用:http : //jsfiddle.net/xQnMd/1/。
$("input").change(function() {
    var file = this.files[0];  // file
        fr   = new FileReader; // to read file contents
    fr.onloadend = function() {
        // get EXIF data
        var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));
        // alert a value
        alert(exif.Make);
    };
    fr.readAsBinaryString(file); // read the file
});
Firefox 26 支持image-orientation: from-image:图像以纵向或横向显示,具体取决于 EXIF 数据。(参见sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation。)
请注意,此属性仅受 Firefox 支持,并且可能会被弃用。
https://github.com/blueimp/JavaScript-Load-Image是一个现代 javascript 库,它不仅可以提取 exif 方向标志 - 它还可以在客户端正确镜像/旋转 JPEG 图像。
我刚刚用这个库解决了同样的问题:JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images
我上传扩展代码以在 html 上正常显示 html 上的照片,在某些 img 标签上正确旋转,特别是对于宽度大于高度的 img 标签。我知道这段代码很丑,但您不需要安装任何其他软件包。(我使用上面的代码来获取 exif 旋转值,谢谢。)
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);
}
var isChanged = false;
function rotate(elem, orientation) {
    if (isIPhone()) return;
    var degree = 0;
    switch (orientation) {
        case 1:
            degree = 0;
            break;
        case 2:
            degree = 0;
            break;
        case 3:
            degree = 180;
            break;
        case 4:
            degree = 180;
            break;
        case 5:
            degree = 90;
            break;
        case 6:
            degree = 90;
            break;
        case 7:
            degree = 270;
            break;
        case 8:
            degree = 270;
            break;
    }
    $(elem).css('transform', 'rotate('+ degree +'deg)')
    if(degree == 90 || degree == 270) {
        if (!isChanged) {
            changeWidthAndHeight(elem)
            isChanged = true
        }
    } else if ($(elem).css('height') > $(elem).css('width')) {
        if (!isChanged) {
            changeWidthAndHeightWithOutMargin(elem)
            isChanged = true
        } else if(degree == 180 || degree == 0) {
            changeWidthAndHeightWithOutMargin(elem)
            if (!isChanged)
                isChanged = true
            else
                isChanged = false
        }
    }
}
function changeWidthAndHeight(elem){
    var e = $(elem)
    var width = e.css('width')
    var height = e.css('height')
    e.css('width', height)
    e.css('height', width)
    e.css('margin-top', ((getPxInt(height) - getPxInt(width))/2).toString() + 'px')
    e.css('margin-left', ((getPxInt(width) - getPxInt(height))/2).toString() + 'px')
}
function changeWidthAndHeightWithOutMargin(elem){
    var e = $(elem)
    var width = e.css('width')
    var height = e.css('height')
    e.css('width', height)
    e.css('height', width)
    e.css('margin-top', '0')
    e.css('margin-left', '0')
}
function getPxInt(pxValue) {
    return parseInt(pxValue.trim("px"))
}
function isIPhone(){
    return (
        (navigator.platform.indexOf("iPhone") != -1) ||
        (navigator.platform.indexOf("iPod") != -1)
    );
}
然后使用诸如
$("#banner-img").change(function () {
    var reader = new FileReader();
    getOrientation(this.files[0], function(orientation) {
        rotate($('#banner-img-preview'), orientation, 1)
    });
    reader.onload = function (e) {
        $('#banner-img-preview').attr('src', e.target.result)
        $('#banner-img-preview').css('display', 'inherit')
    };
    // read the image file as a data URL.
    reader.readAsDataURL(this.files[0]);
});