在客户端使用JavaScript访问JPEG EXIF旋转数据

我想根据JPEG EXIF图像数据中相机设置的原始旋转旋转照片。 诀窍是,所有这些都应该在浏览器中发生,使用JavaScript和<canvas>

JavaScript如何访问JPEG,本地文件API对象,本地<img>或远程<img> ,EXIF数据读取旋转信息?

服务器端的答案不正确; 我正在寻找客户端解决scheme。

如果你只想要方向标签,没有别的,不想包含另一个巨大的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) { 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' /> 

您可以将此库与HTML5 File 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 。)

在Chrome中也有一个bug实现 。

如果你想跨浏览器,最好的办法就是在服务器上做。 你可以有一个API接受一个文件的URL并返回你的EXIF数据; PHP有一个模块 。

这可以使用Ajax来完成,因此对用户来说是无缝的。 如果您不关心跨浏览器兼容性,并且可以依赖HTML5文件function,请查看库JsJPEGmeta ,以便您可以使用本地JavaScript获取数据。

看看我写的模块(你可以在浏览器中使用它),将exif方向转换为CSS转换: https : //github.com/Sobesednik/exif2css

还有这个节点程序生成所有方向的JPEG设备: https : //github.com/Sobesednik/generate-exif-fixtures

我上传扩展代码,以正常的一些img标签与右旋转,尤其是对于宽度比宽度高的img标签,通过Android上的相机在HTML上显示照片。 我知道这个代码是丑陋的,但你不需要安装任何其他的软件包。 (我用上面的代码获取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]); });