我想根据相机在JPEG EXIF图像数据中设置的原始旋转来旋转照片。诀窍是所有这一切都应该在浏览器中进行,使用JavaScript和<canvas>
。
JavaScript如何访问JPEG,本地文件API对象,本地<img>
或远程<img>
,EXIF数据来读取轮换信息?
服务器端答案不正常;我正在寻找客户端解决方案。
答案 0 :(得分:20)
您可以将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
});
答案 1 :(得分:17)
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。
请注意,此属性仅受Firefox支持且为likely to be deprecated。
答案 2 :(得分:11)
https://github.com/blueimp/JavaScript-Load-Image是一个现代的javascript库,它不仅可以提取exif方向标志 - 它还可以在客户端正确镜像/旋转JPEG图像。
我刚刚用这个库解决了同样的问题:JS Client-Side Exif Orientation: Rotate and Mirror JPEG Images
答案 3 :(得分:4)
如果您希望跨浏览器,最好的办法是在服务器上进行。你可以有一个API来获取文件URL并返回EXIF数据; PHP has a module for that。
这可以使用Ajax来完成,因此对用户来说是无缝的。如果您不关心跨浏览器兼容性,并且可以依赖HTML5文件功能,请查看库JsJPEGmeta,以便您可以使用本机JavaScript获取该数据。
答案 4 :(得分:3)
查看我编写的模块(您可以在浏览器中使用它)将exif方向转换为CSS转换:https://github.com/Sobesednik/exif2css
此节点程序还可以生成具有所有方向的JPEG装置:https://github.com/Sobesednik/generate-exif-fixtures
答案 5 :(得分:3)
我上传扩展代码以通过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]);
});
答案 6 :(得分:1)
改进/添加更多功能到早期的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;
}
用法:
import { getOrientation } from './ImageUtils';
...
onDrop = (pics: any) => {
getOrientation(pics[0], rotationInDegrees => {
this.setState({ image: pics[0], rotate: rotationInDegrees });
});
};