2011-09-28 201 views
79

我想根據相機在JPEG EXIF圖像數據中設置的原始旋轉旋轉照片。訣竅是,所有這些都應該發生在瀏覽器中,使用JavaScript和<canvas>在客戶端使用JavaScript訪問JPEG EXIF旋轉數據

JavaScript如何訪問JPEG,本地文件API對象,本地<img>或遠程<img>,EXIF數據讀取旋轉信息?

服務器端答案不正確;我正在尋找客戶端解決方案。

回答

135

如果只想方向標籤,沒有別的,不喜歡有另一個巨大的JavaScript庫我寫一點代碼,只提取方向標籤儘可能快地(它使用數據視圖和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' />

值:

-2: not jpeg 
-1: not defined 

enter image description here

+0

爲2,4,5,7得到正確的圖像,你需要旋轉和翻轉,對不對? –

+0

我的圖像的方向是3 ..我如何設置方向爲1? – Lucy

+0

我試過使用幾個JPG格式,而且大多數都返回-1,不管它們的方向如何。這是否意味着它無法訪問EXIF數據? – SimeriaIonut

4

如果你想跨瀏覽器,最好的辦法就是在服務器上做。你可以有一個接受文件URL並返回EXIF數據的API; PHP has a module for that

這可以使用Ajax來完成,因此它對用戶來說是無縫的。如果您不關心跨瀏覽器兼容性,並且可以依賴HTML5文件功能,請查看庫JsJPEGmeta,以便您可以使用原生JavaScript獲取該數據。

+0

[本文](http://benno.id的最後一部分。 au/blog/2009/12/30/html5-fileapi-jpegmeta)解釋了圖書館的工作原理。 –

+0

謝謝。 JS腳本看起來很甜美。我沒有使用PHP(其實我討厭它),我正在尋找純粹的客戶端JavaScript解決方案。 –

+0

這裏是一個參考實現https://github.com/miohtama/Krusovice/blob/master/src/tools/resizer.js –

20

您可以使用this library結合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 
}); 
+0

謝謝。問題中的JS lib看起來有點過時,但可能會起作用。 –

+0

另請參閱我剛剛寫的一個文件上傳小部件演示。它使用上面提到的EXIF.js庫來讀取圖像文件的元數據中的EXIF方向標誌。根據這些信息,它使用畫布元素應用旋轉... http://sandbox.juurlink.org/html5imageuploader –

+0

試圖在我的項目中包含binaryajax.js導致訪問被拒絕錯誤。 –

15

Firefox 26支持image-orientation: from-image:圖像根據EXIF數據顯示爲縱向或橫向。 (見sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation。)

還有a bug to implement this in Chrome

+2

感謝您的錯誤報告鏈接。我爲它出演過,以便Chrome團隊知道更多人想要這個。 – DemiImp

+0

@Sam Dutton,似乎我需要你的幫助。看看這個:https://stackoverflow.com/questions/45650465/how-do-i-fix-orientation-upload-image-before-saving-in-folder-javascript –

3

退房我寫了一個模塊(你可以在瀏覽器中使用它),它的EXIF方向轉換爲CSS變換:https://github.com/Sobesednik/exif2css

也有這個節點程序生成所有的方向的JPEG裝置:https://github.com/Sobesednik/generate-exif-fixtures

+0

好模塊!但是,它首先從JPEG獲取EXIF信息的方式如何? –

+0

@MikkoOhtamaa感謝和不支持,你必須使用exif-js或exiftool服務器端 – zavr

+0

這很有用。但在我看來,它只適用於人像照片,而不是風景照片。 –

1

我上傳擴展代碼以顯示照片的Android相機上的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]); 

});