在客户端访问 JavaScript 中的 JPEG EXIF 旋转数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7584794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Accessing JPEG EXIF rotation data in JavaScript on the client side
提问by Mikko Ohtamaa
I'd like to rotate photos based on their original rotation, as set by the camera in JPEG EXIF image data. The trick is that all this should happen in the browser, using JavaScript and <canvas>
.
我想根据相机在 JPEG EXIF 图像数据中设置的原始旋转来旋转照片。诀窍是所有这些都应该在浏览器中使用 JavaScript 和<canvas>
.
How could JavaScript access JPEG, a local file API object, local <img>
or remote <img>
, EXIF data to read the rotation information?
JavaScript 如何访问 JPEG、本地文件 API 对象、本地<img>
或远程<img>
、EXIF 数据以读取旋转信息?
Server-side answers are not OK; I am looking for a client-sidesolution.
服务器端的回答不行;我正在寻找客户端解决方案。
回答by Ali
If you only want the orientation tag and nothing else and don't like to include another huge javascript library I wrote a little code that extracts the orientation tag as fast as possible (It uses DataView and readAsArrayBuffer
which are available in IE10+, but you can write your own data reader for older browsers):
如果你只想要方向标签而不想要其他任何东西并且不喜欢包含另一个巨大的 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' />
values:
价值观:
-2: not jpeg
-1: not defined
For those using Typescript, you can use the following code:
对于那些使用 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);
}
回答by pimvdb
You can use the exif-jslibrary in combination with the HTML5 File API: http://jsfiddle.net/xQnMd/1/.
您可以将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
});
回答by Sam Dutton
Firefox 26 supports image-orientation: from-image
: images are displayed portrait or landscape, depending on EXIF data. (See sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation.)
Firefox 26 支持image-orientation: from-image
:图像显示为纵向或横向,具体取决于 EXIF 数据。(参见sethfowler.org/blog/2013/09/13/new-in-firefox-26-css-image-orientation。)
There is also a bug to implement this in Chrome.
Beware that this property is only supported by Firefox and is likely to be deprecated.
请注意,此属性仅受 Firefox 支持,并且可能会被弃用。
回答by flexponsive
https://github.com/blueimp/JavaScript-Load-Imageis a modern javascript library that can not only extract the exif orientation flag - it can also correctly mirror/rotate JPEG images on the client side.
https://github.com/blueimp/JavaScript-Load-Image是一个现代 javascript 库,它不仅可以提取 exif 方向标志 - 它还可以在客户端正确镜像/旋转 JPEG 图像。
I just solved the same problem with this library: JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images
我刚刚用这个库解决了同样的问题:JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images
回答by Alex Turpin
If you want it cross-browser, your best bet is to do it on the server. You could have an API that takes a file URL and returns you the EXIF data; PHP has a module for that.
如果您想要跨浏览器,最好的办法是在服务器上进行。你可以有一个 API 来获取文件 URL 并返回 EXIF 数据;PHP 有一个模块。
This could be done using Ajaxso it would be seamless to the user. If you don't care about cross-browser compatibility, and can rely on HTML5file functionality, look into the library JsJPEGmetathat will allow you to get that data in native JavaScript.
这可以使用Ajax来完成,因此它对用户来说是无缝的。如果您不关心跨浏览器兼容性,并且可以依赖HTML5文件功能,请查看JsJPEGmeta库,它允许您在本机 JavaScript 中获取该数据。
回答by zavr
Check out a module I've written (you can use it in browser) which converts exif orientation to CSS transform: https://github.com/Sobesednik/exif2css
查看我编写的模块(您可以在浏览器中使用它),它将 exif 方向转换为 CSS 转换:https: //github.com/Sobesednik/exif2css
There is also this node program to generate JPEG fixtures with all orientations: https://github.com/Sobesednik/generate-exif-fixtures
还有这个节点程序可以生成所有方向的JPEG夹具:https: //github.com/Sobesednik/generate-exif-fixtures
回答by Wonhyuk Cho
I upload expansion code to show photo by android camera on html as normal on some img tag with right rotaion, especially for img tag whose width is wider than height.I know this code is ugly but you don't need to install any other packages. (I used above code to obtain exif rotation value, Thank you.)
我上传扩展代码以在 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)
);
}
and then use such as
然后使用诸如
$("#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]);
});
回答by Kevin Grant
Improving / Adding more functionality to Ali's answer from earlier, I created a util method in Typescript that suited my needs for this issue. This version returns rotation in degrees that you might also need for your project.
改进/为 Ali 之前的回答添加更多功能,我在 Typescript 中创建了一个 util 方法,以满足我对这个问题的需求。此版本返回您的项目可能还需要的度数旋转。
ImageUtils.ts
/**
* Based on StackOverflow answer: https://stackoverflow.com/a/32490603
*
* @param imageFile The image file to inspect
* @param onRotationFound callback when the rotation is discovered. Will return 0 if if it fails, otherwise 0, 90, 180, or 270
*/
export function getOrientation(imageFile: File, onRotationFound: (rotationInDegrees: number) => void) {
const reader = new FileReader();
reader.onload = (event: ProgressEvent) => {
if (!event.target) {
return;
}
const innerFile = event.target as FileReader;
const view = new DataView(innerFile.result as ArrayBuffer);
if (view.getUint16(0, false) !== 0xffd8) {
return onRotationFound(convertRotationToDegrees(-2));
}
const length = view.byteLength;
let offset = 2;
while (offset < length) {
if (view.getUint16(offset + 2, false) <= 8) {
return onRotationFound(convertRotationToDegrees(-1));
}
const marker = view.getUint16(offset, false);
offset += 2;
if (marker === 0xffe1) {
if (view.getUint32((offset += 2), false) !== 0x45786966) {
return onRotationFound(convertRotationToDegrees(-1));
}
const little = view.getUint16((offset += 6), false) === 0x4949;
offset += view.getUint32(offset + 4, little);
const tags = view.getUint16(offset, little);
offset += 2;
for (let i = 0; i < tags; i++) {
if (view.getUint16(offset + i * 12, little) === 0x0112) {
return onRotationFound(convertRotationToDegrees(view.getUint16(offset + i * 12 + 8, little)));
}
}
// tslint:disable-next-line:no-bitwise
} else if ((marker & 0xff00) !== 0xff00) {
break;
} else {
offset += view.getUint16(offset, false);
}
}
return onRotationFound(convertRotationToDegrees(-1));
};
reader.readAsArrayBuffer(imageFile);
}
/**
* Based off snippet here: https://github.com/mosch/react-avatar-editor/issues/123#issuecomment-354896008
* @param rotation converts the int into a degrees rotation.
*/
function convertRotationToDegrees(rotation: number): number {
let rotationInDegrees = 0;
switch (rotation) {
case 8:
rotationInDegrees = 270;
break;
case 6:
rotationInDegrees = 90;
break;
case 3:
rotationInDegrees = 180;
break;
default:
rotationInDegrees = 0;
}
return rotationInDegrees;
}
Usage:
用法:
import { getOrientation } from './ImageUtils';
...
onDrop = (pics: any) => {
getOrientation(pics[0], rotationInDegrees => {
this.setState({ image: pics[0], rotate: rotationInDegrees });
});
};