Answers:
github项目JavaScript-Load-Image提供了针对EXIF方向问题的完整解决方案,可以针对所有8个exif方向正确旋转/镜像。请参阅javascript exif方向在线演示
该图像被绘制到HTML5画布上。通过画布操作在js / load-image-orientation.js中实现了其正确的呈现。
希望这可以节省其他人的时间,并向搜索引擎介绍这种开源gem :)
canvas.toDataURL()
并在服务器端进行解码和保存。
<img>
标签一样具有响应性?
Mederr的上下文转换效果很好。如果您只需要提取方向,请使用此功能 -您不需要任何EXIF读取库。以下是用于在base64图像中重新设置方向的功能。 这是一个小玩意儿。我还准备了一个带有方向提取演示的小提琴。
function resetOrientation(srcBase64, srcOrientation, callback) {
var img = new Image();
img.onload = function() {
var width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
// set proper canvas dimensions before transform & export
if (4 < srcOrientation && srcOrientation < 9) {
canvas.width = height;
canvas.height = width;
} else {
canvas.width = width;
canvas.height = height;
}
// transform context before drawing image
switch (srcOrientation) {
case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
case 7: ctx.transform(0, -1, -1, 0, height, width); break;
case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
ctx.drawImage(img, 0, 0);
// export base64
callback(canvas.toDataURL());
};
img.src = srcBase64;
};
switch
不需要默认方向的情况,因为该转换不会执行任何操作。另外,考虑使用srcOrientation > 4 && srcOrientation < 9
代替[5,6,7,8].indexOf(srcOrientation) > -1
,因为它速度更快,资源占用更少(包括RAM和CPU)。那里不需要数组。当批处理大量每个位都很重要的图像时,这一点很重要。否则,答案很好。已投票!
indexOf
您的建议相比有多沉重。我运行了一个具有1000万次迭代的简单循环,速度提高了1400%。尼斯:D谢谢一堆!
getOrientation
,我很好奇这在性能方面是否有效。getOrientation
调用fileReader.readAsArrayBuffer
,然后我们调用URL.createObjectURL
并将结果传递到中resetOrientation
,该结果将该URL加载为图像。这是否意味着图像文件将不被浏览器“加载” /读取一次,而是两次,还是我误会了?
image-orientation: from-image
使用CSS的浏览器都支持这种情况。
如果
width = img.width;
height = img.height;
var ctx = canvas.getContext('2d');
然后,您可以使用这些转换将图像转到方向1
从方向:
ctx.transform(1, 0, 0, 1, 0, 0);
ctx.transform(-1, 0, 0, 1, width, 0);
ctx.transform(-1, 0, 0, -1, width, height);
ctx.transform(1, 0, 0, -1, 0, height);
ctx.transform(0, 1, 1, 0, 0, 0);
ctx.transform(0, 1, -1, 0, height, 0);
ctx.transform(0, -1, -1, 0, height, width);
ctx.transform(0, -1, 1, 0, 0, width);
在ctx上绘制图像之前
好的,除了@ user3096626回答,我认为如果有人提供了代码示例,它将更有用,下面的示例将向您展示如何解决来自url(远程图像)的图像方向:
解决方案1:使用javascript(推荐)
由于加载图像库不会仅从url图像(文件/ blob)中提取exif标签,因此我们将同时使用exif-js和加载图像 javascript库,因此请按照以下步骤将这些库添加到您的页面中:
<script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.1.0/exif.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-scale.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-orientation.min.js"></script>
请注意,exif-js的2.2版似乎存在问题,因此我们使用2.1
那么基本上我们将要做的是
a-使用加载图片 window.loadImage()
b-使用以下命令读取exif标签 window.EXIF.getData()
c-将图像转换为画布并使用固定图像方向 window.loadImage.scale()
d-将画布放入文档中
干得好 :)
window.loadImage("/your-image.jpg", function (img) {
if (img.type === "error") {
console.log("couldn't load image:", img);
} else {
window.EXIF.getData(img, function () {
var orientation = EXIF.getTag(this, "Orientation");
var canvas = window.loadImage.scale(img, {orientation: orientation || 0, canvas: true});
document.getElementById("container").appendChild(canvas);
// or using jquery $("#container").append(canvas);
});
}
});
当然,您也可以从canvas对象获取图像为base64并将其放置在img src属性中,因此使用jQuery即可;)
$("#my-image").attr("src",canvas.toDataURL());
这是完整的代码:github:https : //github.com/digital-flowers/loadimage-exif-example
解决方案2:使用html(浏览器黑客)
有一个非常快速简便的技巧,如果直接在新标签页中打开图像而没有任何html,则大多数浏览器会以正确的方向显示图像(大声笑,我不知道为什么),因此基本上您可以使用iframe显示图像通过直接将iframe src属性作为图片网址:
<iframe src="/my-image.jpg"></iframe>
解决方案3:使用CSS(仅iOS上的Firefox和Safari)
有css3属性可以解决图像方向问题,但是它仅在firefox和safari / ios上有效,仍然值得一提,因为它将很快对所有浏览器可用(caniuse的浏览器支持信息)
img {
image-orientation: from-image;
}
对于那些具有输入控件文件的用户,不知道其方向是什么,有点懒惰,并且不想在下面包含一个大型库,@ WunderBart提供的代码与他链接到的答案融合在一起(https://stackoverflow.com/a/32490603)找到方向。
function getDataUrl(file, callback2) {
var callback = function (srcOrientation) {
var reader2 = new FileReader();
reader2.onload = function (e) {
var srcBase64 = e.target.result;
var img = new Image();
img.onload = function () {
var width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
// set proper canvas dimensions before transform & export
if (4 < srcOrientation && srcOrientation < 9) {
canvas.width = height;
canvas.height = width;
} else {
canvas.width = width;
canvas.height = height;
}
// transform context before drawing image
switch (srcOrientation) {
case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
case 7: ctx.transform(0, -1, -1, 0, height, width); break;
case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
ctx.drawImage(img, 0, 0);
// export base64
callback2(canvas.toDataURL());
};
img.src = srcBase64;
}
reader2.readAsDataURL(file);
}
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);
}
可以很容易地这样称呼
getDataUrl(input.files[0], function (imgBase64) {
vm.user.BioPhoto = imgBase64;
});
image/jpeg
而不是默认值image/png
,并将输出图像的大小调整为最大宽度(在我的情况下为400px)产生了很大的差异。(这对于缩略图来说效果很好,但是如果您必须显示完整图像,我建议您避免使用base64并直接将canvas元素直接注入页面中...)
WunderBart的答案对我来说是最好的。请注意,如果图像通常是正确的方向,则可以大大提高速度,只需先测试方向,然后在不需要旋转的情况下绕过其余代码即可。
将来自wunderbart的所有信息放在一起,就像这样;
var handleTakePhoto = function () {
let fileInput: HTMLInputElement = <HTMLInputElement>document.getElementById('photoInput');
fileInput.addEventListener('change', (e: any) => handleInputUpdated(fileInput, e.target.files));
fileInput.click();
}
var handleInputUpdated = function (fileInput: HTMLInputElement, fileList) {
let file = null;
if (fileList.length > 0 && fileList[0].type.match(/^image\//)) {
isLoading(true);
file = fileList[0];
getOrientation(file, function (orientation) {
if (orientation == 1) {
imageBinary(URL.createObjectURL(file));
isLoading(false);
}
else
{
resetOrientation(URL.createObjectURL(file), orientation, function (resetBase64Image) {
imageBinary(resetBase64Image);
isLoading(false);
});
}
});
}
fileInput.removeEventListener('change');
}
// from http://stackoverflow.com/a/32490603
export function getOrientation(file, callback) {
var reader = new FileReader();
reader.onload = function (event: any) {
var view = new DataView(event.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.slice(0, 64 * 1024));
};
export function resetOrientation(srcBase64, srcOrientation, callback) {
var img = new Image();
img.onload = function () {
var width = img.width,
height = img.height,
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
// set proper canvas dimensions before transform & export
if (4 < srcOrientation && srcOrientation < 9) {
canvas.width = height;
canvas.height = width;
} else {
canvas.width = width;
canvas.height = height;
}
// transform context before drawing image
switch (srcOrientation) {
case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
case 7: ctx.transform(0, -1, -1, 0, height, width); break;
case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
default: break;
}
// draw image
ctx.drawImage(img, 0, 0);
// export base64
callback(canvas.toDataURL());
};
img.src = srcBase64;
}
-2
和-1
返回,getOrientation
以便不要尝试旋转没有旋转数据的非jpg图像或图像。
file.slice()
优化功能,但真正的推动力来自使用较小的图像和image/jpeg
输出格式来创建较小的数据URL。(即使是10兆像素的图像,也没有明显的延迟。)
file.slice()
,因为您将阻止对base64图像源的支持。
我还没有看到有人提到browser-image-compression
图书馆。它有一个完美的辅助功能。
用法: const orientation = await imageCompression.getExifOrientation(file)
这样的工具也可以通过许多其他方式使用。
File
据我所知,您无法创建对象。接下来的最好的事情是将图像和方向发送到服务器,并在那里进行文件操作。或者,您可以使用canvas
和toDataURL
方法生成base64字符串。
我创建了一个包装在ES6模块中的类,可以解决此问题。
它是103行,没有依赖关系,并且结构和文档都很好,意味着易于修改/重用。
处理所有8种可能的方向,并且基于Promise。
在这里,希望对您有所帮助:https : //gist.github.com/vdavid/3f9b66b60f52204317a4cc0e77097913
我正在使用混合解决方案(php + css)。
需要容器用于:
div.imgCont2
需要旋转的容器;div.imgCont1
zoomOut-所需的容器width:150%
;div.imgCont
图像为zoomOut时,滚动条所需的容器。。
<?php
$image_url = 'your image url.jpg';
$exif = @exif_read_data($image_url,0,true);
$orientation = @$exif['IFD0']['Orientation'];
?>
<style>
.imgCont{
width:100%;
overflow:auto;
}
.imgCont2[data-orientation="8"]{
transform:rotate(270deg);
margin:15% 0;
}
.imgCont2[data-orientation="6"]{
transform:rotate(90deg);
margin:15% 0;
}
.imgCont2[data-orientation="3"]{
transform:rotate(180deg);
}
img{
width:100%;
}
</style>
<div class="imgCont">
<div class="imgCont1">
<div class="imgCont2" data-orientation="<?php echo($orientation) ?>">
<img src="<?php echo($image_url) ?>">
</div>
</div>
</div>
除了@fareed namrouti的答案,
如果必须从文件输入元素浏览图像,则应使用此选项
<input type="file" name="file" id="file-input"><br/>
image after transform: <br/>
<div id="container"></div>
<script>
document.getElementById('file-input').onchange = function (e) {
var image = e.target.files[0];
window.loadImage(image, function (img) {
if (img.type === "error") {
console.log("couldn't load image:", img);
} else {
window.EXIF.getData(image, function () {
console.log("load image done!");
var orientation = window.EXIF.getTag(this, "Orientation");
var canvas = window.loadImage.scale(img,
{orientation: orientation || 0, canvas: true, maxWidth: 200});
document.getElementById("container").appendChild(canvas);
// or using jquery $("#container").append(canvas);
});
}
});
};
</script>
我已经写了一个小php脚本来旋转图像。确保存储该图像,以便仅在每次请求时重新计算它。
<?php
header("Content-type: image/jpeg");
$img = 'IMG URL';
$exif = @exif_read_data($img,0,true);
$orientation = @$exif['IFD0']['Orientation'];
if($orientation == 7 || $orientation == 8) {
$degrees = 90;
} elseif($orientation == 5 || $orientation == 6) {
$degrees = 270;
} elseif($orientation == 3 || $orientation == 4) {
$degrees = 180;
} else {
$degrees = 0;
}
$rotate = imagerotate(imagecreatefromjpeg($img), $degrees, 0);
imagejpeg($rotate);
imagedestroy($rotate);
?>
干杯