php正确处理照片的旋转问题
前言
iPhone和一些数码相机在拍照的时候往往会在图片里面加入很多的照片信息(exif),比如拍照时间、光圈大小、曝光时间、GSP地理信息以及拍摄时相机倾斜状态等等,这些信息往往会提供给某些系统来对图片进行正确的显示,但是我们在对处理图片的时候通常会丢失掉那些图片信息,这就会导致显示出现问题,很典型的一个显示错误就是对Iphone拍摄的照片进行剪裁之后发现图片旋转了90度,这个时候就需要对图片进行相应的处理。
背景
博主是在一个小项目中遇到的这个问题,这个项目虽然小,但是涉及到的各种坑真的可以写一篇比较长的技术文章了,这里先不描述,后面单独发一篇关于这个项目的文章,把所涉及到的知识点以及坑点一一描述。
这个小项目中遇到的问题之一就是,我需要使用html5的canvas把读取到的图片进行剪裁,然后渲染到画布上。很简单的一个需求,但是在Iphone上测试经常会看到剪裁完之后图片自动旋转了90度,安卓机上不存在这个问题。
分析
之前就遇到过这个问题,iPhone拍摄的照片复制到win7上打开图片就是一个倒过来的,而在手机上看却没有问题,这就是因为exif信息在搞鬼,简单点说exif就是保留了图片的拍摄参数,显示的时候程序会读取图片的exif信息,如果exif信息不存在,那么图片就是按照最原始的形式显示,如果有exif信息,程序就会按照exif来。
这里博主遇到的情况是图片在进行剪裁的时候导致exif信息丢失,本来程序读取exif中图片要求顺时针旋转90°,现在exif丢失,程序读不到,默认图片不用旋转,那么我们看到的图片就是倒着的,这里应该怎么解决呢?
在这个项目中,由于读取的图片存在跨域问题,不能直接对本地的图片进行裁剪,所以是先上传到远程服务器,生成一个远程的地址,然后再用canvas读取远程地址,这样就解决了跨域问题。
现在摆在博主面前就有两种解决方案了
只用exif.js本地读取图片的exif信息,直接在客户端进行判断
上传到服务器的时候对图片进行处理,返回的远程地址已经是处理过的图片
对这两种方案进行对比,第一种方案看起来确实挺适合,直接在前端进行处理,这里会非常节省服务器资源,但是缺点也很明显,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模块里面还有很多有意思的方法,可以拿来做很多有用的东西,比如拿来批量采集并分析图片,提取图片信息等,这里推荐一个好玩的网站 https://exif.cn/
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格式的大小限制就是一个很难被发现的问题,博主在调试的时候对生成的数据进行观察才发现,当出现大图的时候就会转换失败。
ZipArchive类可以直接调用并且实现文件实时压缩了以前做过相关的例子,今天我们来看一篇关于php利用自带的ZipArchive类压缩文件实现方法.php处理压缩和解压缩用自带的扩展类就能实现,虽然没有压缩软件那么高效和丰富,但是在web应用中,还是非常受用的。用php打包一个zip压缩文件非常简单,几行代码就能实现。
实现效果如下:
实现源码:
<?php
$zipObj = new ZipArchive();
$res = $zipObj->open('my.zip', ZipArchive::CREATE); //创建一个zip
if ($res === true)
{ //创建成功
$zipObj->addFile('info.php'); //添加当前目录下的info.php文件
$zipObj->addFile('daixiaorui.zip'); //添加当前目录下的daixiaorui.zip文件
$zipObj->close();
echo 'done~';
}
else
{
echo 'failed';
}
?>
范例比较简单,其实ZipArchive类远不止这些方法,如果读者需要更丰富的功能,请查看文档手册
下面我们来看一篇关于dwz增删改查不局部刷新,希望这篇文章能够帮助到各位朋友,具体的细节步骤如下文介绍.一个小项目,用到了dwz,苦于官方少于维护且需要一部分前端技术一直没有深挖,迫在眉睫,遇到了跟很多人一样的问题,就是增删改查不能局部刷新,苦恼啊。经多多放查找资料和翻阅源码终于皇天不负有心。
项目使用TP3.1.3
需要做一下修改
找到 Thinkphp/lib/core/Action.class.php
查找 ajaxReturn 方法
注释掉
if(func_num_args()>2) {// 兼容3.0之前用法
$args = func_get_args();
array_shift($args);
$info = array();
$info['data'] = $data;
$info['info'] = array_shift($args);
$info['status'] = array_shift($args);
$data = $info;
$type = $args?array_shift($args):'';
}
然后在下方添加
if(method_exists($this,"ajaxAssign")) $this->ajaxAssign($data);//ajax赋值扩展
完整代码如下
/**
* Ajax方式返回数据到客户端
* @access protected
* @param mixed $data 要返回的数据
* @param String $type AJAX返回数据格式
* @return void
*/
protected function ajaxReturn($data,$type='') {
// if(func_num_args()>2) {// 兼容3.0之前用法
// $args = func_get_args();
// array_shift($args);
// $info = array();
// $info['data'] = $data;
// $info['info'] = array_shift($args);
// $info['status'] = array_shift($args);
// $data = $info;
// $type = $args?array_shift($args):'';
// }
if(method_exists($this,"ajaxAssign")) $this->ajaxAssign($data);//ajax赋值扩展
if(empty($type)) $type = C('DEFAULT_AJAX_RETURN');
switch (strtoupper($type)){
case 'JSON' :
// 返回JSON数据格式到客户端 包含状态信息
//header('Content-Type:application/json; charset=utf-8');
header('Content-Type:text/html; charset=utf-8'); //zhanghuihua
exit(json_encode($data));
case 'XML' :
// 返回xml格式数据
header('Content-Type:text/xml; charset=utf-8');
exit(xml_encode($data));
case 'JSONP':
// 返回JSON数据格式到客户端 包含状态信息
header('Content-Type:application/json; charset=utf-8');
$handler = isset($_GET[C('VAR_JSONP_HANDLER')]) ? $_GET[C('VAR_JSONP_HANDLER')] : C('DEFAULT_JSONP_HANDLER');
exit($handler.'('.json_encode($data).');');
case 'EVAL' :
// 返回可执行的js脚本
header('Content-Type:text/html; charset=utf-8');
exit($data);
default :
// 用于扩展其他返回格式数据
tag('ajax_return',$data);
}
}
在任意一出添加
protected function ajaxAssign(&$result){
$result['statusCode'] = $result['status'];
$result['navTabId'] = $_REQUEST['navTabId'];
$result['message']=$result['info'];
}
退出DWZ,重新登录 再试试吧!
function ping($data){ $server_url = array( 'baidu' => 'http://ping.baidu.com/ping/RPC2', // 'google' => 'http://blogsearch.google.com/ping/RPC2', //'bloglines' => 'http://www.bloglines.com/ping' ); $xml = ' weblogUpdates.extendedPing ' . $data['blog_name'] . ' ' . $data['blog_home'] . ' ' . $data['blog_url'] . ' ' . $data['rss_url'] . ' ' $xml_baidu = ' weblogUpdates.extendedPing ' . $data['blog_name'] . ' ' . $data['blog_home'] . ' ' . $data['blog_url'] . ' ' . $data['rss_url'] . ' ' foreach ($server_url as $k => $s){ $s = trim($s); if(!empty($s)){ $c = curl_init(); $header = array( "Content-type: text/xml;charset=\"utf-8\"", "Accept: text/xml" ); curl_setopt _array( $c, array( CURLOPT_URL => $s, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $header, CURLOPT_POSTFIELDS => $k === 'baidu' ? $xml_baidu : $xml ) ); $content=curl_exec($c); var_dump($content); curl_close($c); } } }
相关文章
- 今天小编在这里就来给美图秀秀的这一款软件的使用者们来说下究竟该怎么给照片天空加蓝天白云的教程,各位想知道具体制作步骤的,那么下面就来跟着小编一起看看吧。 ...2016-09-14
- 这篇文章主要介绍了Windows批量搜索并复制/剪切文件的批处理程序实例,需要的朋友可以参考下...2020-06-30
PHP传值到不同页面的三种常见方式及php和html之间传值问题
在项目开发中经常见到不同页面之间传值在web工作中,本篇文章给大家列出了三种常见的方式。接触PHP也有几个月了,本文总结一下这段日子中,在编程过程里常用的3种不同页面传值方法,希望可以给大家参考。有什么意见也希望大...2015-11-24BAT批处理判断服务是否正常运行的方法(批处理命令综合应用)
批处理就是对某对象进行批量的处理,通常被认为是一种简化的脚本语言,它应用于DOS和Windows系统中。这篇文章主要介绍了BAT批处理判断服务是否正常运行(批处理命令综合应用),需要的朋友可以参考下...2020-06-30- js修改input的type属性有些限制。当input元素还未插入文档流之前,是可以修改它的值的,在ie和ff下都没问题。但如果input已经存在于页面,其type属性在ie下就成了只读属性了,不可以修改。...2013-10-19
- 1,utf8_bin跟utf8_general_ci的区别 ci是 case insensitive, 即 "大小写不敏感", a 和 A 会在字符判断中会被当做一样的; bin 是二进制, a 和 A 会别区别对待. 例如你运行: SELECT * FROM table WHERE txt = 'a'...2013-10-04
- 一、1 CREATE TABLE NAME(name VARCHAR(10)); 对这个表,缺省情况下,下面两个查询的结果是一样的:复制代码 代码如下: SELECT * FROM TABLE NAME WHERE name='clip'; SELECT * FROM TABLE NAME WH...2015-03-15
- 一.mysql默认不支持中文,它的server和db默认是latin1编码.所以我们要将其改变为utf-8编码,因为utf-8包含了地球上大部分语言的二进制编码 1.关闭mysql服务 sudo /etc/init.d/mysql stop 2.修改mysql配置文件 mysql配...2015-10-21
- file_get_contents的超时处理话说,从PHP5开始,file_get_content已经支持context了(手册上写着:5.0.0 Added the context support. ),也就是说,从5.0开始,file_get_contents其实也可以POST数据。今天说的这篇是讲超时的,确实在...2013-10-04
- 这篇文章主要介绍了C#多线程中的异常处理操作,涉及C#多线程及异常的捕获、处理等相关操作技巧,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了postgresql 中的时间处理小技巧(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
- 这篇文章主要介绍了Python同时处理多个异常的方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-29
C#异常处理中try和catch语句及finally语句的用法示例
这篇文章主要介绍了C#异常处理中try和catch语句及finally语句的用法示例,finally语句的使用涉及到了C#的垃圾回收特性,需要的朋友可以参考下...2020-06-25- 这篇文章主要介绍了python如何用moviepy对视频进行简单的处理,帮助大家更好的利用python处理视频,感兴趣的朋友可以了解下...2021-03-11
- 这篇文章主要介绍了使用队列(Queue)解决简单的并发问题,讲解的很细致,喜欢的朋友们可以了解一下...2020-06-25
- 这篇文章介绍了C#异常处理,有需要的朋友可以参考一下...2020-06-25
- 2015年7月29日0点起,Windows 10推送全面开启,Windows7、Windows8.1用户可以免费升级到Windows 10,用户也可以通过系统升级到Windows10,在这过程中,用户会遇到这样那样的问题,下面小编总结了windows 10 安装和使用中5个常见问题,需要的朋友可以参考下...2016-01-27
- 这篇文章主要给大家介绍了关于sql server日志处理不当造成的隐患的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用sql server具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-07-11
- PHP的session功能,一直为许多的初学者为难。就连有些老手,有时都被搞得莫名其妙。本文,将这些问题,做一个简单的汇总,以便大家查阅。 1. 错误提示 引用 代...2016-11-25
- Spring MVC是Spring系列框架中使用频率最高的部分。不管是Spring Boot还是传统的Spring项目,只要是Web项目都会使用到Spring MVC部分。因此程序员一定要熟练掌握MVC部分。本篇博客简要分析Spring MVC处理一个请求的流程。...2021-02-06