原创文章 · 手机与浏览器

手机照片为什么在网页里会"转两遍"或方向错乱:EXIF方向的坑

Jinlab · 2026年10月1日 · 基于真实开发与真机测试整理

竖着拍的手机照片,在手机相册里明明是正的,传到某个网页工具里却横了过来;或者在网页里看着是正的,导出后又歪了。这类问题的根源几乎都是同一个小东西:照片里的EXIF方向标记。这篇文章讲清它是什么,也记录我们在做图片工具pixforge时踩到的一个"转了两遍"的老bug,以及它为什么能躲过测试。

手机拍照时,方向其实没有"转"过

手机的相机传感器是固定朝向的。你把手机竖过来拍,传感器拍下来的像素仍然是按"横着"的顺序排列的。为了让你看到的照片是正的,手机会在文件里写一个小标记,叫EXIF方向(Orientation),取值从1到8:1表示不用转,3表示转180度,6和8表示顺时针或逆时针转90度,其余的2、4、5、7还带有镜像翻转。

也就是说,一张竖拍的照片,文件里存的像素可能是4032×3024的横图,外加一句"显示时请顺时针转90度"。看图软件读到这句话,就会把它摆正成3024×4032。近几年的主流浏览器,在网页里显示一个普通的图片标签时,默认也会照做。

问题出在"不照做"或者"做了两次"的地方:

  • 不照做:有些老软件、某些服务端的图片处理库,只读像素、不看这个标记,照片就横着躺在那里。
  • 做了两次:浏览器已经帮你转正了,你自己的代码又按标记转了一遍,结果又转歪了。
  • 标记和像素对不上:像素已经被旋转过,旧的方向标记却还留在文件里,下一个读到它的软件会再转一次。

我们踩到的坑:以为关掉了浏览器的自动转正

要在网页里处理图片,一般要把文件解码成一张位图,再放到画布上编辑。我们最初的设计文档里写得很明确:浏览器解码时会自动按EXIF转正,如果我们再手动转一次就会转两遍,所以解码时要用createImageBitmap的imageOrientation: "none"选项,显式关掉浏览器的自动转正,由我们自己按标记转一次。

这个设计在纸面上无懈可击,代码里也有注释,单元测试也有,覆盖了各种方向值的变换。可是上线后过了一段时间,我们才发现:带方向标记的竖拍照片,在产品里一直被转了两遍。

我们是在给广色域功能写端到端测试时发现的。用一张48×32像素、方向值为6的小JPEG实测:在当时的Chromium上,imageOrientation设成"none"和"from-image",解码出来都是32×48,也就是浏览器仍然自动转正了。我们的代码紧接着又转了一次,预览就显示成了48×32,竖拍照变成了横的。

教训:设计文档里"这个选项会关掉自动转正",是我们没有验证过的假设。它在当时的Chromium上不成立。依赖浏览器某个选项的具体行为时,必须用真实浏览器和真实格式的文件跑一遍。

为什么它能躲这么久

回头看,原因很朴素:

  • 单元测试只验证了"给定方向值,变换算得对不对",没有碰浏览器解码那一段。
  • 端到端测试上传的全是没有EXIF的PNG,从来没有传过一张真正带方向标记的JPEG。
  • 浏览器解码那几行"胶水代码",因此从来没有被真正跑过。

写测试的人(我们自己)心里想的是"转正这件事有测试",实际上有测试的只是算法,不是整条路径。最后是一次全图比对才把它抓出来:测试把结果和"原始解码再做同样变换"的参考图比尺寸,尺寸对不上,才有了前面那组32×48对48×32的发现。

我们的修法:不依赖浏览器怎么想

修法不是去猜浏览器的行为,而是让结果不依赖它:解码之前,先把文件里的方向标记改写成1(不用转),再交给浏览器解码。这样不管浏览器会不会自动转正,它拿到的都是"已经是正的"的声明,什么也不会做;真正的转正,只由我们自己的一处代码做一次。

这样做还有两个好处:

  • 广色域照片走的是另一条路径,用单独的解码器解出原始色值,这个解码器完全不会自动转正。浏览器路径和这条路径共用同一份"按方向变换"的代码,行为才能一致。
  • 方向标记不只存在于JPEG:PNG用的是eXIf数据块,WebP用的是EXIF数据块,我们对这三种格式一视同仁。改写PNG的数据块之后还要重新计算它的校验值(CRC),否则文件会被判定为损坏。

有一点容易忽略:方向值为2(水平镜像)、3(180度)、4(垂直镜像)这类变换不会改变图片的宽高。如果只靠"看尺寸有没有变"来检查,转了两遍和转了零遍在尺寸上看不出区别,必须比像素。

修复之后,怎么确认真的好了

我们用同一张48×32、方向值为6的JPEG再测:修复后,Playwright驱动的Chromium和那台安卓手机上的Chrome,预览都显示成32×48,竖的。PNG和WebP里的方向标记走同一套处理,也有单元测试覆盖。

要说清楚我们没做到的部分:目前带真实方向标记的JPEG端到端测试,只覆盖了方向5和6;2、3、4、7、8这几个值,只有单元测试,还没有用真实文件做端到端验证。这件事已经记在我们的待办里。

给不同的人

  • 普通用户:如果一张照片在某个工具里横着或倒着,先别怀疑照片坏了。在工具里把它转正再保存,或者换一个会读取方向标记的软件打开。
  • 网页开发者:要么完全信任浏览器的自动转正,要么在解码前抹掉方向标记、自己转一次,不要两头都做;导出时不要再带着旧的方向标记;对JPEG、PNG、WebP都要处理。
  • 测试:用真实文件测。可以用编码器现场生成一张带方向标记的JPEG,覆盖全部8个方向值,并且与"原始像素加同样变换"的参考图做全图比较,而不是抽几个点看一眼。

这些处理已用在Jinlab的图片工具pixforge里。相关文章:手机拍的广色域(Display P3)照片,为什么在网页里处理后颜色变淡?怎么保住?