月度归档:2013年11月

微信扫一扫安全入口边的那些事

二维码越来越多,厕所里、地铁里、公交上、商店里、大厦等等地方均可看到二维码的踪影,二维码已悄然进入我们的生活。解析二维码需要扫码工具,常见的有“我查查”、“手机QQ”、“微信”等应用,微信作为一个用户量庞大的应用,其扫一扫功能自然而然也成为扫码工具中的大哥大。

记得以前网站刚普及的时候,时不时总能看到标题带有”XX误上钓鱼网站XXX“、”某某借助欺诈网站进行XXXX“等新闻。过了不久,各大主流浏览器和IM产品纷纷施招,对用户访问的网址进行安全过滤来体现自身的安全。典型的有Chrome浏览器的安全提示、QQ聊天框的安全网址提示、淘宝旺旺链接的安全提示等。

现在,时不时就有新闻说”乱扫二维码中毒“、”乱扫二维码资金资金被恶意盗取“,可想而知,这些扫码工具必将挡在前面当”保安“,大家也肯定会找一个值得信赖的扫码工具。这不,看微信扫一扫安全过滤功能的频繁改动就知道了。

微信扫一扫功能早已加入安全网址判断功能。如果你的网址是来自腾讯旗下的网站,可直达功能页,微信直接认定为安全网址。如果你的网址是来自外来链接(非腾讯旗下网站),微信会自动启用安全网址判断,针对知名网站或已通过腾讯安全机制判断的可信网站网址,微信会提示”已检测到地址:http://www.xxx.com,是否打开“;而针对大多数企业或中小型网站,微信从最开始的提示”是否打开此链接“到”该网页非微信官方网页,是否继续访问“到现在的”该网页非微信官方网页,将由微信转换为手机预览模式。“无论哪个环节哪种提示,都可见微信的用心良苦。

注意:如果近期你发现扫描一个二维码,出现503错误提示,只能说明你扫描的这个网址里面做了跳转或重定向处理之类的,所以…“你被503了”。解决方法很简单,就是把这个过渡页做成类似本篇文章最后那样,引导用户到达真正的页面。

随着微信扫一扫功能的深度优化,对外链安全机制的升级,相信我们会越来越少看到类似这样的新闻标题了:”微信逢码就扫? 小心二维码“有毒”“、”微信二维码成诈骗新途径“等等等。。。

微信的这种安全过滤,一方面是保障了使用微信扫一扫功能用户的安全,另一方面则给那些借助微信营销的企业增加了门槛。对于企业,通过二维码入口再而利用微信朋友圈的营销推广,是最直接、最有效的推广的方法。如我的一个项目“微联”,很多类似这样(http://www.4p.cn/sh/100001)这样的微网站(微网页)网址,借助二维码,通过微信扫一扫进入此类网址,然后将该网站、网页内容推荐至微信朋友圈,从而实现微信营销。

那些想利用微信进行营销的企业、商家,如何跨过微信扫一扫这道安全门槛呢?

方法是有的,最有效的方式是你的网址能在微信安全网址的白名单。但目前微信还没有公布微信扫一扫功能安全网址的判断依据,只能从以前的腾讯产品进行推断。比如腾讯QQ的聊天框网址的安全判断,针对外来网址,它判断安全的主要依据是来自某机构认证过的可信网站。微信想必也八九不离十,这个值得我们思考与验证,更加期待微信能公布或指引企业网站进行安全认证,早日进入微信扫一扫的白名单里。

那么,除了进入安全网址的白名单,难道就没有其他好的办法吗?当然有,这也是我要分享的这篇文章的原因所在。这里我提供2种比较实用的方法,方法1比较没有技术含量,但可能会被一些用户忽略,本人比较喜欢方法2。

方法1:在你的每个二维码最下面标注:如果用微信扫一扫进行扫描,请记得点击”查看原网页“。(这个方法我觉得有点雷,但也值得一试。)

方法2:在二维码进入的网址显示页,加入程序的判断和提示,指引用户访问原始网页。

我们需要了解下微信扫一扫“继续访问”这个按钮点击后的去向以及网址结构,才能更好的做好方法2。

假设你的二维码内容为一个网址,网址为:http://www.4p.cn/sh/100001,用户通过微信扫一扫会出现如下页面及提示:

应用宝截屏2013112702111111111

点击“继续访问”后,实际网址已经变为类似:

http://support.weixin.qq.com/cgi-bin/mmsupport-bin/getpagedata?requrl=http://www.4p.cn/sh/100001?sukey=446546a7dd62a96761d62bed9809be5c968a285c81659bb96e5bd4fe3b73d915bbc8a01f281b1fbf3238e1212d7cff87&t=favorites

这样的网址!

大家看到了吧,实际上微信已经是通过自己的网站将你要访问的网址进行内容抓取并展示。经测试,微信只给这些网站保留3样东西:文字内容、图片内容、链接。也就是说,你原本网站的样式、页面特效等都被过滤了,只纯粹的把内容展示出来。

如图,原本我的页面效果可能是这样:

应用宝截屏2013112701111122

但通过微信扫一扫访问会变成这样:

应用宝截屏201311270222222222222

微信已经把你网站代码过滤得一干二净了,只留下了纯文本信息,这样无论从美观上、体验上都不理想。

那我们如何引导用户访问原始网页呢?

我们不讨论是否能用技术手段实现让微信自动跳转(当然也是没必要这样做的,腾讯总有办法过滤掉你的代码,毕竟是通过访问它的网址来加载你的内容,而不是你自己网址的直接展示),但我们可以在页面上加上一些温馨提示,可用图、用文字来指引用户访问我们高质量的页面。注意,微信转化后的图片,也是经过处理的,经过测试,对于jpg、png等图片,基本都是切成宽度为200px高自适应的图片,对于gif,会切成宽度199px的图片,大家在做图片提示的时候,尽量用这两个宽度来制作较好。

我们可以做一个中间转换页进行温馨提示或在当前页面加上提示,告诉用户,为了更好的体验网站功能,为了更好的查看相关内容,请点击“查看原始网页”。这样一来,用户即便通过微信扫一扫访问到了一个已被微信转换过的页面,也不会对你的网页内容大打折扣,同时也能实现网站原本就能实现的功能,如地图导航、幻灯片等等效果。以下是我现做的文字提示放出来提供参考(建议大家做图片形式的温馨提示较好较美观),其中需要获取微信url的值进行链接处理,这方面请自行根据我上面贴出的网址进行深究,有更好方法的,请回复并告知我。截图如下:

应用宝截屏2013112702333

 

最后,我已经把上面的文字信息换成了以下这个gif图:

注意:微信扫一扫入口今天又修改了规则,在顶部出现:查看原网页的链接,原页面所有链接将失效,并且能转化跳转后的页面了。以上提供方法,仅仅作为参考了,图片内容可以改成:为了更好的体验,请点击顶部的“查看原网页”

原创文章,转载请注明: 转载自蔡洁锐的blog

本文链接地址: 微信扫一扫安全入口边的那些事

利用域名的泛解析加上IIS配置实现windows主机的二级域名转向

写在前面:

网站有N个客户,想给每个客户都配置独立的二级域名?想让别人访问二级域名就能达到你指定的某个网站的某个目录?你用的是IIS而不是apache?你不想一个二级域名就多配置一个网站吧?好吧,看完此文你就能实现这种效果了。(此文针对windows下的IIS Rewrite展开说明,apache的同理,但没IIS复杂。)

至于如何在windows下安装Rewrite实现URL重定向可看我之前写的文章:http://www.caijierui.com/blog/?p=26

原理:

利用泛域名的解析,在服务器进行URL转向处理并转向指定的目录。

我们需要什么?

拥有域名解析权限:你能管理自己的域名,能解析二级域名(如*.caijierui.com)

有用1台服务器 或 1个能让你配置IIS的虚拟空间(甭想了)

两个网站:1个空白默认80端口的网站 + 你的网站(如caijierui.com)

如何开始?

1:首先,我们要先配置域名,如你之前的网站caijierui.com的a解析是121.198.211.250。那么你需要进行泛域名解析,我知道你在问什么是泛域名解析(泛域名解析简单点说就是做个a记录,内容为:*.caijierui.com 121.198.211.250,这样一来我随便什么二级域名,如XX.caijierui.com xxxx.caijierui.com blog.caijieurui.com w32342.caijieuri.com都指向了121.198.211.250这台服务器)。

2:先ping一下,随便输入一个二级域名,测试下是否IP为你刚设置的A记录中的IP地址?如果是,咱们再继续第三步(A记录生效一般是10分钟-12小时不等- -这个我也不知道为啥,反正你就先等个半天一天的吧)

3:在IIS中配置一个空白网站(TCP端口默认80,主机头留空的网站)。网上很多教程都是说,必须整台服务器都只有你一个网站才能实现二级域名的跳转,实际上是不需要的。只要IIS中有1个是空白的网站即可。我们需要做的是,配置这个空白网站的Rewrite,让所有指向这台服务器的域名都能重定向。

4:下载mrewrite压缩包:http://pan.baidu.com/s/1kots1,然后配置空白网站在IIS中的ISPI筛选器,至于如何配置,请看:http://www.caijierui.com/blog/?p=26这篇文章,这里就不多说了,记得要重启iis。注意:如果你的网站之前已经做过IIS Rewrite设置了,请保持和网站的Rewrite非同一个(建议直接放到空白网站的根目录下)要不然会……出现无限循环。

5:打开httpd.ini,输入以下内容即可:

[ISAPI_Rewrite]
# 3600 = 1 hour
CacheClockRate 3600
RepeatLimit 32
#进行二级域名转向,所有域名都指向让类似这样的二级域名:xxx.caijierui.com 转向 www.caijierui.com/xxx/
RewriteCond Host: (.*)\.(.*)\.((com)|(com\.cn)|(com\.hk)|(cn))
RewriteRule (.*)$ http://www\.caijierui\.com/$1/ [I,R,L]

6:至此,配置完成,已能实现二级域名转向一级域名的某个目录。

后语:

大家也能看到,实际上那样设置还没法满足太多的商业要求?对吧,例如你想截取url的id值呢?你用Rewrite去实现,很麻烦- -还不如直接在默认网站底下用ASP或PHP文件来实现跳转,具体的大家可以自由发挥。以下放出比较简单的代码用PHP实现301跳转:

<?php
$the_host = $_SERVER['HTTP_HOST'];//取得当前域名
$the_ehost= $substr($the_host,0,$strripos($the_host,".caijierui"));//截取.caijierui.com前面的二级域名
$the_val = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';//判断地址后面部分
$the_val = strtolower($the_val);//将英文字母转成小写
if($the_val=="/index.php")//判断是不是首页
{$the_val="";//如果是首页,赋值为空}
if($the_ehost !== 'www')//如果域名不是带www的网址那么进行下面的301跳转
{
header('HTTP/1.1 301 Moved Permanently');//发出301头部
header('Location:http://www.caijierui.com'.$the_ehost."/".$the_val);//跳转到带www的网址
}
?>

 

原创文章,转载请注明: 转载自蔡洁锐的blog

本文链接地址: 利用域名的泛解析加上IIS配置实现windows主机的二级域名转向

thinkphp ajax上传图片处理方法(可用)

有一个网站项目用的thinkphp框架,不喜欢thinkphp的跳转提示,想改成全站ajax提交。

他碰到一个问题,就是用ajax提交的方式上传图片是无法上传成功的。

由于时间关系,我就直接参与进去,查了下thinkphp的资料,看了这哥们用iframe实现了:http://borissun.iteye.com/blog/1151350

但直接粘贴代码后发现问题多多,没法实现上传。同时也看到官方:http://www.thinkphp.cn/code/245.html也很多人说无法上传。

我想估计是粘贴的时候少了什么代码之类的,或修改之后出问题了,但这个思路是很正确的。于是我就在这代码的基础上,继续修改成网站项目需要的。

以下贴出代码,可实现ThinkPHP的ajax的上传图片操作,但这些代码基于上传功能的测试做的,很多地方,包括样式、安全过滤等方面还没考虑周全,如果要用,大家需要自行修改。

第一步:

在模板页<body>处添加如下代码:

<form id="formImg" action="__URL__/ajaximg" method="post" target="hidden_frame" enctype="multipart/form-data">
 <div>
 <input type="hidden" name="sh_id" id="sh_id" value="{$id}">
<!--这里的{$id}大家可以随便填1个进去-->
 <input id="AidImg" type="file" name="AidImg" onchange="uploadImg()"/>
 <div style="display:none;" id="imgError">图片不可为空</div>
 <iframe style="display:none" name='hidden_frame' id="hidden_frame"></iframe>
 <div><img id="p_img" src="" width="80" height="80"/> </div>

 <span class="help_inline">尺寸:80*80</span>

 </div>
 </form>

模板页里的JS代码:

function uploadImg() 
 {var names=$("#AidImg").val().split("."); 
 if(names[1]!="gif"&&names[1]!="GIF"&&names[1]!="jpg"&&names[1]!="JPG"&&names[1]!="png"&&names[1]!="PNG") 
 {$("#imgError").html("<span>"+"图片必须为gif,jpg,png格式"+"</span>");
 $("#formImg .help-inline").hide(); 
 $("#imgError").show(); 
 return; 
 }
 $("#formImg").submit();
 $("#imgError").show();
 $("#imgError").html("图片上传中ing"); 
 } 
 function callback(message,success,lujing) 
 { 
 if(success==false) 
 { 
 $("#imgError").html("<span>"+message+"</span>"); 
 $("#imgError").show(); 
 } 
 else{ 
 $("#imgError").hide(); 
 $(".fromtrs").show();
 $("#formImg .help-inline").hide(); 
 var paths=lujing; 
 $("#p_img").attr("src",lujing+message); 
 $("#p_img").attr("imgname",message); //这里由于数据库里只存入图片名称加后缀名,故和路径拆开了
 } 
 }

PHP里的代码:

public function ajaximg(){
 $cpimg_n=$_POST['cpimg_n'];
 if($_FILES["AidImg"]["size"]){
 //如果有上传动作
 if($_FILES["AidImg"]["size"]!=0) {
 $uploadPath = "./Tpl/Public/Uploads/".$sh_id."/product/";
 if ($_FILES["AidImg"]["size"] < 1024 * 1024 * 2){ 
 if ($_FILES["AidImg"]["error"] > 0) {echo "<script>parent.callback('发生未知错误上传失败,请重新上传',false)</script>";} 
 else {$suffix = substr($_FILES["AidImg"]["name"], strrpos($_FILES["AidImg"]["name"], '.') + 1);
 if($cpimg_n){//如果是修改产品 --这个地方大家不用理会- -根据自己需要修改
 $cpimg_n = substr($cpimg_n, 0,strrpos($cpimg_n, '.'))."g"; 
 $name = $cpimg_n."." .$suffix;
 }
 else{
 //如果是添加产品
 $cpid=100000; //这个根据自己需要请修改
 $name = $sh_id ."_".$cpid. "." .$suffix;
 }
 if (!is_dir($uploadPath)) {
 //没有目录创建目录
 if(mkdir($uploadPath,0777)){echo("<script>parent.callback('正在为图片创建目录',false)</script>");}else{echo("<script>parent.callback('无法创建该商户图片目录,请联系网站管理员',false)</script>");exit;}
 } 
 if (move_uploaded_file($_FILES["AidImg"]["tmp_name"], $uploadPath . "/" . $name)) {
 //如果已经移动成功了
 $lujingg="/Tpl/Public/Uploads/".$sh_id."/product/";//传过去的路径
 $imgname=$name;//传过去的图片名入库用
 echo "<script>parent.callback('".$imgname."',true,'".$lujingg."')</script>"; exit;
 } 

 }
 }//end if ($_FILES["AidImg"]["size"] < 1024 * 1024 * 2)
 else {echo "<script>parent.callback('图片大小不能超过2M',false)</script>";return;} 
 }//end if($_FILES["AidImg"]["size"]!=0)
 else{echo "<script>parent.callback('无此路径',false)</script>";exit;}
 } //end if($_FILES["AidImg"]["size"])
 else{echo "<script>parent.callback('请上传图片',false)</script>"; }
}//end public function ajaximg()

以上是单纯ajax上传图片的方法,已测试OK。

实际使用过程中,可能页面中还有其他的input要结合一起提交。这时候需要分为2步骤完成,第一步是点击上传图片(这时候还没有数据库操作,仅仅是生成图片。也就是上面说的ajax上传图片操作);第二步,将图片名称和其他的input值一起传过去入库操作即可。

顺便贴一个ajax提交的例子:

function ajax_form()
 { var id=$("#id").val();
 var p_title=$("#p_title").val();
 var p_img=$("#p_img").attr("imgname");
 var p_summary=$("#p_summary").val();
 var sh_id=$("#sh_id").val();
 var login_user_id=$("#login_user_id").val();
 var baocun=$("input[type=submit]").attr("baocun");
 if(baocun=='1'){alert("已添加成功,请勿重复操作");
 window.location="__URL__/sjcp/id/"+login_user_id;
 }
 var p_summary= $(document.getElementsByTagName('iframe')[1].contentWindow.document.body).html();//这玩意是编辑器,这里是第二个iframe,第一个就是上传图片那个iframe
 var url="__URL__/XXXXXXXX";
$.ajax({
 type: "POST",
 timeout: 10000, 
 url: url,
 data:{id:id,p_title:p_title,p_img:p_img,p_summary:p_summary,sh_id:sh_id},
 cache:false,
 beforeSend: function(){
 $('[tishi=true]').html("保存中");
 },
 error: function(){
 $('[tishi=true]').html("保存失败");
 },
 success: function(data){
 $('[tishi=true]').html(data);
 $("input[type=submit]").attr("baocun","1");
 }
 });
 }

 

原创文章,转载请注明: 转载自蔡洁锐的blog

本文链接地址: thinkphp ajax上传图片处理方法(可用)

利用百度地图api做一个手机终端导航地图

百度API功能强大,但如果用手机终端的sdk(ios/android/塞班等)开发接入,考虑到多终端兼容,免去各种判断以及其他复杂流程,还不如用html5调用js api,里面的定位功能可能稍微逊色点,但无所谓,只要把关键字查询、点击查询这两个功能一起加上,基本上就可以形成导航了(不是真正意义上的带语音或实时更新的导航)。

开始制作之前,咱们先来看下要实现什么功能以及采用哪个便捷框架。

功能概述:

1:可获取当前用户GPS坐标,并提供该位置到达终点的所有公交车/地铁、驾车导航路线参考。

2:可搜索相关地点名称显示该地点至终点的路线图,并提供公交车/地铁、驾车导航路线参考。

3:可在地图点击某位置,获取该位置信息并提供该位置到达终点的所有公交车/地铁、驾车导航路线参考。

4:公交车/地铁、驾车两种模式的路线图可随时切换显示,起点与终点不变。

名词解析:

终点:是固定的一个点,例如某个公司、某个商户的地址。

起点:默认获取用户当前位置,如无法获取则可通过输入地址或点击地图生成。

异常和正常处理:

1:当无法获取用户GPS位置时(多种原因造成,如超时、不支持、拒绝等原因),提示“无法获取当前位置”,并默认起点为“北京”;

2:当成功获取用户GPS位置时,提示获取成功并显示获取到的GPS位置附近街道名称。

前端框架采用:

jQuery Mobile(不采用也行,只要调用jquery.js即可,我是为了方面显示以及页面美观- -)

实现过程:

Head部分,这部分没什么好说的,就是加载js和css,值得注意的是里面的api密匙,大家记得修改成自己的,别用我的,指不定哪天我就删了。。。

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>手机终端调用百度地图API实例 - caijierui.com</title>
<link href="http://www.caijierui.com/jquery-mobile/jquery.mobile-1.3.2.min.css" rel="stylesheet" type="text/css">
<script src="http://www.caijierui.com/jquery-mobile/jquery-1.9.1.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=50bc3fec4dce26a0ea12b1b62d0ab29a"></script>
<!--这里的ak后面的参数,是我的API密匙,请自行更换-->
<style type="text/css">
#l-map h1 {
 margin-top:5px;
}
</style>
<script src="http://www.caijierui.com/jquery-mobile/jquery.mobile-1.3.2.min.js" type="text/javascript"></script>
</head>

body部分:

<body>
<div data-role="content">
 <h3>手机终端展示页导航</h3>
 <!--
 这部分HTML有2个input是必须有的,可以改样式等,但id不要变,如果要改变id以及某些属性,请自行查看js进行修改。
 说明:id为dizhi的input,里面的value值JS有用
 id为gjc的input,里面的startzb、gps、值JS有用
 如更换请记得调用2个方法onClick="btnbus_onclick()"和onClick="jiache()"
 -->
 <p id="tishi">正在获取您的位置......为了更好的体验本功能,如提示获取您地理位置的信息,请您同意"共享位置"。</p>
 <label>我想从这里出发:</label>
 <input type="search" name="search" value="" id="dizhi" data-inline="true" />
 <input type="submit" name="submit" value="公共交通工具" id="gjc" onClick="btnbus_onclick()" startzb="北京" data-icon="search" data-iconpos="left" data-inline="true" gps='' />
 <input type="submit" name="submit" value="驾车/出租车" onClick="jiache()" data-icon="search" data-iconpos="left" data-inline="true" />
 <!--这部分显示百度地图内容-->
 <div id="r-result"></div>
 <div id="l-map" style="width:100%;min-height:300px;border:1px solid #ddd;float:left;"></div>
 <!--//这部分显示百度地图内容-->
 <script type="text/javascript">

 //实例化地图,这里的l-map就是上面的div ID
 var map = new BMap.Map("l-map");

 //路线信息展示框的ID,也就是上面的DIVid
 var lxkuang="r-result";

 //终点坐标,此部分可自由设置动态获取
 var zb="116.425199,39.914005";
 </script>
 <!--调用我的JS,上面实际引用时,记得修改终点坐标。-->
 <script type="text/javascript" src="http://www.caijierui.com/js/bdapiforjerry.js"></script>
</div>
<div data-role="footer" data-theme="a" data-position="fixed" data-id="footernav">
 <div data-role="navbar">
 <ul>
 <li><a href="http://www.caijierui.com/blog" data-ajax='false'>我的博客</a></li>
 <li><a href="http://www.caijierui.com" data-ajax='false'>联系方式</a></li>
 <li><a href="#" class="ui-btn-active ui-state-persist" onClick="jiache(0)">地图路线</a></li>
 <li><a href="http://www.caijierui.com/blog/?p=66" data-ajax='false'>使用说明</a></li>
 </ul>
 </div>
 <!-- /navbar --></div>
</div>
</body>

上面用到的bdapiforjerry.js可点击这里http://www.caijierui.com/js/bdapiforjerry.js下载

可自行根据自己的喜好修改代码,其中要注意:

点击地图获取坐标并查找周边街道的部分代码,需要将“if(bs==’0′){jiache()}else if(bs==’1′){btnbus_onclick()}else{alert(“发生错误,获取不到交通方式”)};”这句放在if(result)里,不然的话。。。你会发现程序会先调用jiache()和btnbus_onclick()然后才写入坐标($(“#dizhi”).val(addComp.city+addComp.district+”, “+addComp.street);)

实际效果查看

http://www.caijierui.com/mydemo/baidumap.html

JS里面代码现写现传,难免有一些不够简练,大家可以多多交流。

 

原创文章,转载请注明: 转载自蔡洁锐的blog

本文链接地址: 利用百度地图api做一个手机终端导航地图

PHP QRcode+ImageMagick生成带圆角自动居中logo的二维码

用ImageMagick生成缩略图和处理图片,加水印等都不错,效率快质量高。

关于ImageMagick的一些介绍和安装,请查看ImageMagick安装及使用说明(windows版)

现在大街小巷到处是二维码,在二维码如此猖狂的时代,不自己生成几个二维码总觉得很别扭。。。

用PHP QRcode生成二维码是很简单的一件事,可以到http://sourceforge.net/p/phpqrcode/wiki/Home/查看demo以及下载安装。

原理很简单:用QRcode生成二维码、获取二维码图片并添加水印(水印图分为2张,1张背景圆角图+1张logo)

PHP QRcode生成二维码大致如下:

<?php
require '../phpqrcode.php';

// 二维码数据
$data = 'http://www.caijierui.com/';

// 纠错级别:L、M、Q、H
$level = 'H';

// 点的大小:1到10,用于手机端4就可以了
$pointsize = 4;
$path = "images/";

//生成的文件名
$fileName = $path.$pointsize.'.png';

//生成图片
QRcode::png($data, $fileName, $level, $pointsize);//如果不生成图片,把$fileName 改成 false即可

//echo "<img src='".$filename."'/>";
?>

要在二维码中间加LOGO,必须是先生成二维码图片,然后将二维码图片用imagemagick进行加水印处理。

上一步,咱们获取到图片路径为:$filename,用imagemagick配合处理的代码如下:

 require '../imagick_class.php';
 $image = new imagick_class(); 
 $image->open($filename);
 $image->resize_to(464,464,'scale_fill',array(255,255,255,0));//按比例压缩成464*464的图片,如超出范围则留白。
 $image->get_img('./Uploads/yuanjiao.png', 184, 184);//添加水印,X坐标和Y坐标均为184
 $image->get_img('./Uploads/logo.jpg', 184, 184,true);//后面的true表明需要剪裁
 $image->save_to('./Uploads/dailogodeerweima.jpg');//保存图片

上面提到的圆角图片可以从这里下载:yuanjiao_104x104(右键另存为图片)

imagick_class里面的内容如下:

<?
/*此类由一个叫Lou Barnes的人编写的,从网上下载的,觉得还能用,我在原有基础上,添加一个函数来针对二维码生成。*/
class imagick_class 
{ 
 private $image = null; 
 private $type = null; 
 // 构造函数 
 public function __construct(){} 
 // 析构函数 
 public function __destruct() 
 { 
 if($this->image!==null) $this->image->destroy(); 
 } 

 // 载入图像 
 public function open($path) 
 { 
 $this->image = new Imagick( $path ); 
 if($this->image) 
 { 
 $this->type = strtolower($this->image->getImageFormat()); 
 } 
 return $this->image; 
 } 

 public function crop($x=0, $y=0, $width=null, $height=null) 
 { 
 if($width==null) $width = $this->image->getImageWidth()-$x; 
 if($height==null) $height = $this->image->getImageHeight()-$y; 
 if($width<=0 || $height<=0) return; 

 if($this->type=='gif') 
 { 
 $image = $this->image; 
 $canvas = new Imagick(); 

 $images = $image->coalesceImages(); 
 foreach($images as $frame){ 
 $img = new Imagick(); 
 $img->readImageBlob($frame); 
 $img->cropImage($width, $height, $x, $y); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 $canvas->setImagePage($width, $height, 0, 0); 
 } 

 $image->destroy(); 
 $this->image = $canvas; 
 } 
 else 
 { 
 $this->image->cropImage($width, $height, $x, $y); 
 } 
 } 

 /* 
 * 更改图像大小 
 $fit: 适应大小方式 
 'force': 把图片强制变形成 $width X $height 大小 
 'scale': 按比例在安全框 $width X $height 内缩放图片, 输出缩放后图像大小 不完全等于 $width X $height 
 'scale_fill': 按比例在安全框 $width X $height 内缩放图片,安全框内没有像素的地方填充色, 使用此参数时可设置背景填充色 $bg_color = array(255,255,255)(红,绿,蓝, 透明度) 透明度(0不透明-127完全透明)) 
 其它: 智能模能 缩放图像并载取图像的中间部分 $width X $height 像素大小 
 $fit = 'force','scale','scale_fill' 时: 输出完整图像 
 $fit = 图像方位值 时, 输出指定位置部分图像 
 字母与图像的对应关系如下: 

 north_west north north_east 

 west center east 

 south_west south south_east 

 */ 

 public function resize_to($width = 100, $height = 100, $fit = 'center', $fill_color = array(255,255,255,0) ) 
 { 

 switch($fit) 
 { 
 case 'force': 
 if($this->type=='gif') 
 { 
 $image = $this->image; 
 $canvas = new Imagick(); 

 $images = $image->coalesceImages(); 
 foreach($images as $frame){ 
 $img = new Imagick(); 
 $img->readImageBlob($frame); 
 $img->thumbnailImage( $width, $height, false ); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 } 
 $image->destroy(); 
 $this->image = $canvas; 
 } 
 else 
 { 
 $this->image->thumbnailImage( $width, $height, false ); 
 } 
 break; 
 case 'scale': 
 if($this->type=='gif') 
 { 
 $image = $this->image; 
 $images = $image->coalesceImages(); 
 $canvas = new Imagick(); 
 foreach($images as $frame){ 
 $img = new Imagick(); 
 $img->readImageBlob($frame); 
 $img->thumbnailImage( $width, $height, true ); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 } 
 $image->destroy(); 
 $this->image = $canvas; 
 } 
 else 
 { 
 $this->image->thumbnailImage( $width, $height, true ); 
 } 
 break; 
 case 'scale_fill': 
 $size = $this->image->getImagePage(); 
 $src_width = $size['width']; 
 $src_height = $size['height']; 

 $x = 0; 
 $y = 0; 

 $dst_width = $width; 
 $dst_height = $height; 

 if($src_width*$height > $src_height*$width) 
 { 
 $dst_height = intval($width*$src_height/$src_width); 
 $y = intval( ($height-$dst_height)/2 ); 
 } 
 else 
 { 
 $dst_width = intval($height*$src_width/$src_height); 
 $x = intval( ($width-$dst_width)/2 ); 
 } 

 $image = $this->image; 
 $canvas = new Imagick(); 

 $color = 'rgba('.$fill_color[0].','.$fill_color[1].','.$fill_color[2].','.$fill_color[3].')'; 
 if($this->type=='gif') 
 { 
 $images = $image->coalesceImages(); 
 foreach($images as $frame) 
 { 
 $frame->thumbnailImage( $width, $height, true ); 

 $draw = new ImagickDraw(); 
 $draw->composite($frame->getImageCompose(), $x, $y, $dst_width, $dst_height, $frame); 

 $img = new Imagick(); 
 $img->newImage($width, $height, $color, 'gif'); 
 $img->drawImage($draw); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 $canvas->setImagePage($width, $height, 0, 0); 
 } 
 } 
 else 
 { 
 $image->thumbnailImage( $width, $height, true ); 

 $draw = new ImagickDraw(); 
 $draw->composite($image->getImageCompose(), $x, $y, $dst_width, $dst_height, $image); 

 $canvas->newImage($width, $height, $color, $this->get_type() ); 
 $canvas->drawImage($draw); 
 $canvas->setImagePage($width, $height, 0, 0); 
 } 
 $image->destroy(); 
 $this->image = $canvas; 
 break; 
 default: 
 $size = $this->image->getImagePage(); 
 $src_width = $size['width']; 
 $src_height = $size['height']; 

 $crop_x = 0; 
 $crop_y = 0; 

 $crop_w = $src_width; 
 $crop_h = $src_height; 

 if($src_width*$height > $src_height*$width) 
 { 
 $crop_w = intval($src_height*$width/$height); 
 } 
 else 
 { 
 $crop_h = intval($src_width*$height/$width); 
 } 

 switch($fit) 
 { 
 case 'north_west': 
 $crop_x = 0; 
 $crop_y = 0; 
 break; 
 case 'north': 
 $crop_x = intval( ($src_width-$crop_w)/2 ); 
 $crop_y = 0; 
 break; 
 case 'north_east': 
 $crop_x = $src_width-$crop_w; 
 $crop_y = 0; 
 break; 
 case 'west': 
 $crop_x = 0; 
 $crop_y = intval( ($src_height-$crop_h)/2 ); 
 break; 
 case 'center': 
 $crop_x = intval( ($src_width-$crop_w)/2 ); 
 $crop_y = intval( ($src_height-$crop_h)/2 ); 
 break; 
 case 'east': 
 $crop_x = $src_width-$crop_w; 
 $crop_y = intval( ($src_height-$crop_h)/2 ); 
 break; 
 case 'south_west': 
 $crop_x = 0; 
 $crop_y = $src_height-$crop_h; 
 break; 
 case 'south': 
 $crop_x = intval( ($src_width-$crop_w)/2 ); 
 $crop_y = $src_height-$crop_h; 
 break; 
 case 'south_east': 
 $crop_x = $src_width-$crop_w; 
 $crop_y = $src_height-$crop_h; 
 break; 
 default: 
 $crop_x = intval( ($src_width-$crop_w)/2 ); 
 $crop_y = intval( ($src_height-$crop_h)/2 ); 
 } 

 $image = $this->image; 
 $canvas = new Imagick(); 

 if($this->type=='gif') 
 { 
 $images = $image->coalesceImages(); 
 foreach($images as $frame){ 
 $img = new Imagick(); 
 $img->readImageBlob($frame); 
 $img->cropImage($crop_w, $crop_h, $crop_x, $crop_y); 
 $img->thumbnailImage( $width, $height, true ); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 $canvas->setImagePage($width, $height, 0, 0); 
 } 
 } 
 else 
 { 
 $image->cropImage($crop_w, $crop_h, $crop_x, $crop_y); 
 $image->thumbnailImage( $width, $height, true ); 
 $canvas->addImage( $image ); 
 $canvas->setImagePage($width, $height, 0, 0); 
 } 
 $image->destroy(); 
 $this->image = $canvas; 
 } 

 } 

 // 添加水印图片 这个为未其他用途,针对二维码请看下一个添加水印
 public function add_watermark($path, $x = 0, $y = 0) 
 { 
 $watermark = new Imagick($path); 
 $draw = new ImagickDraw(); 
 $draw->composite($watermark->getImageCompose(), $x, $y, $watermark->getImageWidth(), $watermark->getimageheight(), $watermark); 

 if($this->type=='gif') 
 { 
 $image = $this->image; 
 $canvas = new Imagick(); 
 $images = $image->coalesceImages(); 
 foreach($image as $frame) 
 { 
 $img = new Imagick(); 
 $img->readImageBlob($frame); 
 $img->drawImage($draw); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 } 
 $image->destroy(); 
 $this->image = $canvas; 
 } 
 else 
 { 
 $this->image->drawImage($draw); 
 } 
 } 

 // 添加水印文字 
 public function add_text($text, $x = 0 , $y = 0, $angle=0, $style=array()) 
 { 
 $draw = new ImagickDraw(); 
 if(isset($style['font'])) $draw->setFont($style['font']); 
 if(isset($style['font_size'])) $draw->setFontSize($style['font_size']); 
 if(isset($style['fill_color'])) $draw->setFillColor($style['fill_color']); 
 if(isset($style['under_color'])) $draw->setTextUnderColor($style['under_color']); 

 if($this->type=='gif') 
 { 
 foreach($this->image as $frame) 
 { 
 $frame->annotateImage($draw, $x, $y, $angle, $text); 
 } 
 } 
 else 
 { 
 $this->image->annotateImage($draw, $x, $y, $angle, $text); 
 } 
 } 

 // 保存到指定路径 
 public function save_to( $path ) 
 { 
 if($this->type=='gif') 
 { 
 $this->image->writeImages($path, true); 
 } 
 else 
 { 
 $this->image->writeImage($path); 

 } 
 } 

 // 输出图像 
 public function output($header = true) 
 { 
 if($header) header('Content-type: '.$this->type); 
 echo $this->image->getImagesBlob(); 
 } 

// 添加水印图片 针对二维码
 public function get_img($path, $x = 0, $y = 0,$qie=false) 
 { //jerry 20131101

 $watermark = new Imagick($path);
 if($qie==true){$watermark->thumbnailImage(92,92,true);
 $height= $watermark->getimageheight();//获取图片高度
 $width= $watermark->getImageWidth();//获取图片宽度
 if($height!=92)$y=$y+(92-$height)/2;//居中处理
 if($width!=92)$x=$x+(92-$width)/2;//居中处理

 }//生成缩略图
 else{$watermark->thumbnailImage(104,104,true);
 $x=$x-6;$y=$y-6;
 }//生成缩略图
//以上的92和104均为宽高,可自行设置或作为值传到get_img(),这个请自由发挥。
 //$watermark->setImageResolution(0.01,0.03);//分辨率
/*如果不需要圆边?那就用下面这个直接生成带边线的也行*/
 //$color='rgb(220, 220, 220)';
 //$color=new ImagickPixel(); 
 //$color->setColor($color); 
 //$watermark->borderImage('rgb(225,255,255)', 2, 2); //内边白边
 //$watermark->borderImage($color, 1, 1); //加边外边灰线

 $draw = new ImagickDraw(); 
 $draw->composite($watermark->getImageCompose(), $x, $y, $watermark->getImageWidth(), $watermark->getimageheight(), $watermark); //合并

 if($this->type=='gif') 
 { 
 $image = $this->image; 
 $canvas = new Imagick(); 
 $images = $image->coalesceImages(); 
 foreach($image as $frame) 
 { 
 $img = new Imagick(); 
 $img->readImageBlob($frame); 
 $img->drawImage($draw); 

 $canvas->addImage( $img ); 
 $canvas->setImageDelay( $img->getImageDelay() ); 
 } 
 $image->destroy(); 
 $this->image = $canvas; 
 } 
 else 
 { 
 $this->image->drawImage($draw); 
 } 

 ///////////

 }

 public function get_width() 
 { 
 $size = $this->image->getImagePage(); 
 return $size['width']; 
 } 

 public function get_height() 
 { 
 $size = $this->image->getImagePage(); 
 return $size['height']; 
 } 

 // 设置图像类型, 默认与源类型一致 
 public function set_type( $type='png' ) 
 { 
 $this->type = $type; 
 $this->image->setImageFormat( $type ); 
 } 

 // 获取源图像类型 
 public function get_type() 
 { 
 return $this->type; 
 } 

 // 当前对象是否为图片 
 public function is_image() 
 { 
 if( $this->image ) 
 return true; 
 else 
 return false; 
 } 

 public function thumbnail($width = 100, $height = 100, $fit = true){ $this->image->thumbnailImage( $width, $height, $fit );} // 生成缩略图 $fit为真时将保持比例并在安全框 $width X $height 内生成缩略图片 

 /* 
 添加一个边框 
 $width: 左右边框宽度 
 $height: 上下边框宽度 
 $color: 颜色: RGB 颜色 'rgb(255,0,0)' 或 16进制颜色 '#FF0000' 或颜色单词 'white'/'red'... 
 */ 
 public function border($width, $height, $color='rgb(220, 220, 220)') 
 { 
 $color=new ImagickPixel(); 
 $color->setColor($color); 
 $this->image->borderImage($color, $width, $height); 
 }
}
?>

原创文章,转载请注明: 转载自蔡洁锐的blog

本文链接地址: PHP QRcode+ImageMagick生成带圆角自动居中logo的二维码

ImageMagick安装及使用说明(windows版)

1.) 下载php_imagick_st-Q16.dll(解压zip出dll)并拷贝php_imagick_st-Q16.dll到 PHP安装目录下的ext目录下(如php-5.2.14-Win32\ext)

这里有必要说明一下:

php_imagick_st-Q16.dll (16位色,包含imagemagic)

php_imagick_dyn-Q16.dll (16位色, 不包含imagemagic)

其中不包含imagemagic的dll文件还需要另外下载 imagemagic安装

大家谷歌或百度一下上面两个文件的区别,大多是说dyn包含imagemagick,而st的不包含。我的st文件是4M,而dyn只有200多K,果断判定st是包含imagemagick而dyn反而不包含。当然也不排除我当年下载这文件的时候,别人故意改名了。其实我09年用到的,最近只是二维码需要这方面,所以才有继续折腾,为了避免下一次配置麻烦,就写了下来。

2.) 编辑 php.ini(文件名可能会不同,例如用PHPNOW安装的PHP,该文件名为php-apache2handler.ini) 添加一个新的extension

;加载imagick.dll
extension=php_imagick_st-Q16.dll

3.) 保存PHP.INI,重启APACHE.

4.) 通过 phpinfo()函数,应该可以查看imagick状态为 启用了.

原创文章,转载请注明: 转载自蔡洁锐的blog

本文链接地址: ImageMagick安装及使用说明(windows版)