php中照片旋转 (orientation) 问题的正确处理


Posted in PHP onFebruary 16, 2017

前言

iPhone和一些数码相机在拍照的时候往往会在图片里面加入很多的照片信息(exif),比如拍照时间、光圈大小、曝光时间、GSP地理信息以及拍摄时相机倾斜状态等等,这些信息往往会提供给某些系统来对图片进行正确的显示,但是我们在对处理图片的时候通常会丢失掉那些图片信息,这就会导致显示出现问题,很典型的一个显示错误就是对Iphone拍摄的照片进行剪裁之后发现图片旋转了90度,这个时候就需要对图片进行相应的处理。

背景

博主是在一个小项目中遇到的这个问题,这个项目虽然小,但是涉及到的各种坑真的可以写一篇比较长的技术文章了,这里先不描述,后面单独发一篇关于这个项目的文章,把所涉及到的知识点以及坑点一一描述。

这个小项目中遇到的问题之一就是,我需要使用html5的canvas把读取到的图片进行剪裁,然后渲染到画布上。很简单的一个需求,但是在Iphone上测试经常会看到剪裁完之后图片自动旋转了90度,安卓机上不存在这个问题。

分析

之前就遇到过这个问题,iPhone拍摄的照片复制到win7上打开图片就是一个倒过来的,而在手机上看却没有问题,这就是因为exif信息在搞鬼,简单点说exif就是保留了图片的拍摄参数,显示的时候程序会读取图片的exif信息,如果exif信息不存在,那么图片就是按照最原始的形式显示,如果有exif信息,程序就会按照exif来。

这里博主遇到的情况是图片在进行剪裁的时候导致exif信息丢失,本来程序读取exif中图片要求顺时针旋转90°,现在exif丢失,程序读不到,默认图片不用旋转,那么我们看到的图片就是倒着的,这里应该怎么解决呢?

在这个项目中,由于读取的图片存在跨域问题,不能直接对本地的图片进行裁剪,所以是先上传到远程服务器,生成一个远程的地址,然后再用canvas读取远程地址,这样就解决了跨域问题。

现在摆在博主面前就有两种解决方案了

  1. 只用exif.js本地读取图片的exif信息,直接在客户端进行判断
  2. 上传到服务器的时候对图片进行处理,返回的远程地址已经是处理过的图片

对这两种方案进行对比,第一种方案看起来确实挺适合,直接在前端进行处理,这里会非常节省服务器资源,但是缺点也很明显,canvas对图片的渲染有一定的限制,当图片过大的时候渲染就会失败,而在这个项目中客户要求最后生成的图片一定要高清大图,上传的照片一般都有3~5M大小,这么大的图片大多数时候canvas都无法处理。

exif.js的原理其实比较简单,就是把图片转换成base64字符串,对其中的信息进行解析,所以一旦要对大图进行处理的时候就会出现问题,数据量太大,导致失败。

第一种方案不可行就只能选择第二种方案了,项目后端使用php做中转处理,前面说了需要生成一个远程地址,php负责把接收到的图片存储在远程服务器,然后返回一个地址给前端,这里只需要在存储之前对图片做处理就可以解决这个问题。

解决

首先给出完整代码,然后对代码进行解释

include_once ("../weixin/jssdk.php");
$jssdk = new JSSDK("wx**************", "******************************");
$access_token = $jssdk->getAccessToken();

$media_id = $_POST['i'];
$savePathFile = '/upload/temps/'.date('YmdHis').rand(1000,9999).'.jpg';
$targetName = __DIR__.$savePathFile;

$str = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=".$access_token."&media_id=".$media_id;
$image = imagecreatefromstring(file_get_contents($str));
$exif = exif_read_data($str);

if(!empty($exif['Orientation'])) {
 switch($exif['Orientation']) {
 case 8:
  $image = imagerotate($image,90,0);
  break;
 case 3:
  $image = imagerotate($image,180,0);
  break;
 case 6:
  $image = imagerotate($image,-90,0);
  break;
 }
}

imagejpeg($image,$targetName);
echo json_encode(array("code"=>0,'d'=>$exif['Orientation'],"path"=>$savePathFile));

看起来确实比较简单,主要还是调用了一些内部函数,处理起来就方便多了。

这个项目使用了微信的上传接口,所以php需要从微信的临时服务器上把用户上传的图片取回来,通过file_get_contents方法就能快速拿到,当然也可以用curl来做。接着使用imagecreatefromstring创建一个图片的缓存,正常情况下如果不需要对图片进行处理,那么接下来就可以直接存储了,这里我们还需要对图片进行一些操作。

通过 exif_read_data 方法可以直接读取到图片的exif信息。

注意:exif_read_data 这个方法是exif扩展里面的方法,如果不能执行,请检查扩展是否安装,是否开启等。

exif模块里面还有很多有意思的方法,可以拿来做很多有用的东西,比如拿来批量采集并分析图片,提取图片信息等。

if(!empty($exif['Orientation'])) {
 switch($exif['Orientation']) {
 case 8:
  $image = imagerotate($image,90,0);
  break;
 case 3:
  $image = imagerotate($image,180,0);
  break;
 case 6:
  $image = imagerotate($image,-90,0);
  break;
 }
}

这一段就很容易看懂了,就是判断图片的旋转状态,对图片进行旋转处理imagerotate方法很好用,当然同类的还有好多函数,可以去了解下,如果你正在做图片处理,这些函数应该会有很大的帮助。

最后使用 imagejpeg 方法把处理过的图片写入到磁盘,然后返回一个包含远程地址的json到前端。

注意:我这里没有做异常捕获,正常情况下文件io操作都必须要做异常捕获,这里的代码只为了说明使用方法,所以省略了这一步。

总结

在这个小项目中,最为常见的问题就是在php函数的使用上,同一种效果可以使用多个函数进行处理,选择一个简洁高效的函数非常重要,在使用第三方扩展的时候务必确认扩展是否安装,扩展依赖的插件是否安装,是否已经开启,是否有其他额外的附加条件等。

前端方面需要知道所使用的js插件有那些附加功能,如果api文章不够清晰可以直接打开插件源码,通常情况下,一个出色的插件往往会在未压缩的代码里面写明所有接口的使用方法,以及注意事项,使用条件等。

此外,还需要对不常见的情况进行判定,当出现一些诡异bug的时候就应该考虑是否是因为当前所给的参数不符合规范,或者超出了规范允许的范围,上面说道的图片转base64格式的大小限制就是一个很难被发现的问题,博主在调试的时候对生成的数据进行观察才发现,当出现大图的时候就会转换失败。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

PHP 相关文章推荐
Win2003服务器安全加固设置--进一步提高服务器安全性
May 23 PHP
在WordPress中安装使用视频播放器插件Hana Flv Player
Jan 04 PHP
thinkphp修改配置进入默认首页的方法
Feb 07 PHP
THINKPHP截取中文字符串函数实例代码
Mar 20 PHP
php 从指定数字中获取随机组合的简单方法(推荐)
Apr 05 PHP
php批量修改表结构实例
May 24 PHP
PHP基于MySQLI函数封装的数据库连接工具类【定义与用法】
Aug 11 PHP
PHP四种排序算法实现及效率分析【冒泡排序,插入排序,选择排序和快速排序】
Apr 27 PHP
ThinkPHP框架获取最后一次执行SQL语句及变量调试简单操作示例
Jun 13 PHP
Laravel框架实现抢红包功能示例
Oct 31 PHP
PHP的重载使用魔术方法代码实例详解
Feb 26 PHP
如何理解PHP核心特性命名空间
May 28 PHP
php使用PDO事务配合表格读取大量数据插入操作实现方法
Feb 16 #PHP
php的PDO事务处理机制实例分析
Feb 16 #PHP
php使用include 和require引入文件的区别
Feb 16 #PHP
Yii2实现UploadedFile上传文件示例
Feb 15 #PHP
使用PHPMailer发送邮件实例
Feb 15 #PHP
php使用gd2绘制基本图形示例(直线、圆、正方形)
Feb 15 #PHP
php使用GD2绘制几何图形示例
Feb 15 #PHP
You might like
Php做的端口嗅探器--可以指定网站和端口
2006/10/09 PHP
PHP实现的激活用户注册验证邮箱功能示例
2017/06/06 PHP
Laravel 之url参数,获取路由参数的例子
2019/10/21 PHP
Aster vs Newbee BO3 第二场2.18
2021/03/10 DOTA
<script defer> defer 是什么意思
2009/05/10 Javascript
js与jquery中获取当前鼠标的x、y坐标位置的代码
2011/05/23 Javascript
javascript 使td内容不换行不撑开
2012/11/29 Javascript
浅析js中2个等号与3个等号的区别
2013/08/06 Javascript
Javascript写入txt和读取txt文件示例
2014/02/12 Javascript
avalon js实现仿google plus图片多张拖动排序附源码下载
2015/09/24 Javascript
Angular.js基础学习之初始化
2017/03/10 Javascript
Angularjs自定义指令实现分页插件(DEMO)
2017/09/16 Javascript
使用vue-aplayer插件时出现的问题的解决
2018/03/02 Javascript
webpack HappyPack实战详解
2019/10/08 Javascript
如何基于layui的laytpl实现数据绑定的示例代码
2020/04/10 Javascript
使用js获取身份证年龄的示例代码
2020/12/11 Javascript
[09:37]2018DOTA2国际邀请赛寻真——不懈追梦的Team Serenity
2018/08/13 DOTA
python实现分析apache和nginx日志文件并输出访客ip列表的方法
2015/04/04 Python
Python发送email的3种方法
2015/04/28 Python
python3 读写文件换行符的方法
2018/04/09 Python
关于python下cv.waitKey无响应的原因及解决方法
2019/01/10 Python
详解Python中的正斜杠与反斜杠
2019/08/09 Python
浅析python标准库中的glob
2020/03/13 Python
python实现用户名密码校验
2020/03/18 Python
属性与 @property 方法让你的python更高效
2020/09/21 Python
HTML5 绘制图像(上)之:关于canvas元素引领下一代web页面的问题
2013/04/24 HTML / CSS
Zooplus葡萄牙:欧洲领先的网上宠物商店
2018/07/01 全球购物
介绍一下gcc特性
2012/01/20 面试题
优秀的导游求职信范文
2014/04/06 职场文书
乡镇党的群众路线教育实践活动个人整改方案
2014/10/31 职场文书
2014年话务员工作总结
2014/11/19 职场文书
2014初中数学教研组工作总结
2014/12/19 职场文书
招标保密承诺书
2015/01/20 职场文书
建筑安全员岗位职责
2015/02/15 职场文书
2015年学校体育工作总结
2015/04/22 职场文书
党小组评议意见
2015/06/02 职场文书