QQ空间的“神奇”图片
近几天好多朋友问我qq空间出现的神奇图片原理,最近比较烦,事情比较多,一直没理。加上我对PHP之类的语言也一知半解。
今天闲了看了一下QQ空间,发现这个很早以前就有人写过这样的帖子了
看别人解释
(转载1非原创)
昨晚上偶然看到个比较坑爹的日志,正文有一张图片,在好友动态列表查看的时候可以显示自己(不是发日志的好友,是你自己)的头像和QQ号码、昵称。正文的文字称,这是“本年度最给力神奇魔力日志(转载会看到你最熟悉的身影)”,并且在文章底部附加了一个发广告的QQ号码。
由于这张坑爹图片的存在,文章转发量短短三天内过千。这招可比那些疯狂加群发广告的来的更有创意和杀伤力,转载传播图片(包括日志当中的广告)的人几乎是全网用户(跟所谓病毒营销差不多了)。好奇的话可以看这篇日志最初来源:hXXp://user.qzone.qq.com/732678621/blog/1363502247
一看就知道是检测referer的把戏。早在几年前BBS还在流行的时候,很多人设置的签名图具有天气预报、客户端信息(浏览器、操作系统、IP所在地之类)、随机笑话、倒计时等五花八门的功能。这都得益于服务器端脚本的图像处理功能。而客户端的检测则是基于HTTP请求中的UserAgent和Referer等信息。
但是印象中QQ空间为了防止referer潜在的安全问题和防止图片被防盗链瞎了很大功夫,凡是发表到QQ空间的日志,正文都会把引用到的所有第三方图片资源缓存到腾讯的云端上。所以直接在日志正文中引用的图片,是不会提交REFER到我们的服务器脚本上的。
文中特别称,“请转载后用电脑进入个人中心看”。为什么要特别说明是“个人中心”呢?我刷新了好久的动态,终于看到了图片所说的效果。页面生成的DOM代码为:
onload="QZFL.media.reduceImage(0,400,300,{trueSrc:'http:\/\/qq.sennvwu.com\/qzone\/do.php',callback:function(img,type,ew,eh,o){var _h = Math.floor(o.oh/o.k),_w = Math.floor(o.ow/o.k);if(_w<=ew && _h<=eh){var p=img.parentNode;p.style.width=_w+'px';p.style.height=_h+'px';}}})"
width="400">
原来QQ空间还是会显示源地址的图片的,仅限于在“个人中心”。这时候请求图片附带的HTTP_REFERER的值为
http://user.qzone.qq.com/QQ号/infocenter
号码就是这样提取到的。如果REFERER不满足条件,这个php将在header中发送Location跳转到同一目录下的no.png。
那么后台是如何取到QQ头像、昵称等信息的呢?我Google到了一个腾讯的WebService接口:
http://base.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=QQ号
不需要任何凭证信息即可获取uins指定的QQ号码的头像、昵称信息,返回的格式为JSON。另外上面的图片还有一个显示地理位置和ISP的功能,这个就比较常见了。我找到了一个比较好用的接口,来自TB:
http://ip.taobao.com/service/getIpInfo.php?ip=127.0.0.1
格式同样也是JSON。
接下来实现这个效果就比较简单了,通过REFERER检测用户的QQ号码,然后在后台下载头像、昵称等信息,用GD函数绘制上图片,返回客户端。
我也折腾了一个‘神奇图片“发到空间,居然捉弄了一群人。下面是php语言的实现代码。为了减少后端的流量,对下载的头像做了缓存处理:
<?php
error_reporting(0);
ob_start();
header('Content-Type: image/png');
define('IMG_NO', "no.png"); #刚开始显示的提示信息
define('IMG_BACKGROUND', "background.png");
define('IMG_WIDTH', 400);
define('IMG_HEIGHT', 128);
define('FONT_NAME', "AdobeHeitiStd-Regular.otf"); #字体文件名
define('CACHE_PATH', rtrim(realpath("./cache"), '/').'/'); #缓存目录
define('CACHE_EXPIRE', 60*60); #缓存时间,单位秒
#(!is_dir(CACHE_PATH) && is_writable(CACHE_PATH)) || die;
/*
$remote: 远程URL
$local: 本地缓存路径
$expire: 过期时间。为-1时,永久不更新缓存
*/
function load_from_cache($remote, $local, $expire = CACHE_EXPIRE, $as_path = false) {
#过滤潜在的危险字符
$local = preg_replace("/[.\/\\\?\*\'\"\|\:\<\>]/", "_", $local);
$cache = CACHE_PATH.$local;
#查找缓存
if(file_exists($cache) && ($expire = -1 || filemtime($cache) - time() < $expire))
return $as_path ? $cache : file_get_contents($cache);
#文件不存在或缓存过期,重新下载
$content = file_get_contents($remote);
file_put_contents($cache, $content);
return $as_path ? $cache : $content;
}
/*
返回客户端信息。
*/
function client_info() {
$url = "http://ip.taobao.com/service/getIpInfo.php?ip=";
$ip = ($_SERVER["HTTP_VIA"] && $_SERVER["HTTP_X_FORWARDED_FOR"] ?
$_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER["REMOTE_ADDR"]);
$info = explode('"', load_from_cache($url.$ip, $ip, -1));
$string = $info[7].$info[23].$info[31].$info[47];
return json_decode('"'.$string.'"');
}
$referer = $_SERVER['HTTP_REFERER'];
#$referer = "http://user.qzone.qq.com/123456789/infocenter";
$pattern = "/http:\/\/user.qzone.qq.com\/(\d+)\/infocenter/";
if(preg_match($pattern, $referer, $matches)) {
#获取QQ号码
$uin = $matches[1];
$info = explode('"', load_from_cache(
"http://base.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins=".$uin, $uin));
$avatar = $info[3];
$nickname = iconv("GBK", "UTF-8//IGNORE", $info[5]);
$client = client_info();
#重点来了,生成图片
try{
$im = imagecreatefrompng(IMG_BACKGROUND);
#绘制头像
$avatar_file = load_from_cache($avatar, $uin.".jpg", 60*60*24, true);
$im_avatar = imagecreatefromjpeg($avatar_file);
imagecopymerge($im, $im_avatar, 14, 14, 0, 0, 100, 100, 100);
imagedestroy($im_avatar);
#绘制文字
$blue = imagecolorallocate($im, 0, 0x99, 0xFF);
$white = imagecolorallocate($im, 0xFF, 0xFF, 0xFF);
$texts = array(
array(12, 148, 40, $white, $uin),
array(18, 125, 70, $blue, $nickname),
array(16, 125, 100, $blue, $client)
);
foreach($texts as $key=>$value) {
imagettftext($im, $value[0], 0, $value[1], $value[2], $value[3], FONT_NAME,
mb_convert_encoding($value[4], "html-entities", "utf-8")); #解决乱码问题
}
imagepng($im);
imagedestroy($im);
header("Content-Length: ".ob_get_length());
ob_end_flush();
} catch (Exception $e) {
#die($e->getMessage());
$error = true;
}
} else {
$error = true;
}
if($error){
header('Content-Length: '.filesize(IMG_NO));
echo file_get_contents(IMG_NO);
}
这个估计理解起来有难度:
简单来说
QQ空间的“神奇”图片的更多相关文章
- Android NineGridLayout — 仿微信朋友圈和QQ空间的九宫格图片展示自定义控件
NineGridLayout 一个仿微信朋友圈和QQ空间的九宫格图片展示自定义控件. GitHub:https://github.com/HMY314/NineGridLayout 一.介绍 1.当只 ...
- QQ空间首页背景图片淡出解析与不足完善
一件事情的发生总是有原因的,当然更多的是对技术本身的追求,一定要搞懂啦,废话不多说,大宝剑直插主题. 起因 以前做过一个xx项目,在登陆界面背景图片中,直接引用了一张大图,css类似于这样(backg ...
- 仿qq空间相册的图片批量上传
效果: 转载请注明:http://www.cnblogs.com/TheViper/ 主要是flash组件的tilelist,这个很有用.还有对flash组件源码的一点修改hack. 还是代码很简单, ...
- 使用PHP打造QQ空间神奇图片
说明 你一定在qq空间遇到过这样的东西:打开一张图片,上面有你的QQ号和昵称,你觉得很神奇,是不是? 其实原理很简单,那张图片是动态生成的,上面显示的信息是根据你访问的Url获得的,然后用程序动态的画 ...
- 【web前端优化之图片模糊到清晰】看我QQ空间如何显示相片
前言 此篇文章估计不会太长,有移除首页的风险,但是老夫(称老夫是因为我们真正的叶小钗其实都100多岁啦)是不会怕滴.所以,我来了哟! 题外话:今天我们一起还看了一道前端的面试题,而后我本来还想多找几道 ...
- jQuery插件实现图片展开效果,jquery.gallery。仿腾讯QQ空间说说图片展示效果。
公司的项目http://www.umfun.com/,有个说说的页面(和腾讯QQ空间说说一样),里面有个发表图片功能,上传完图片,需要点击展开的效果. 当时手里面事情比较多(公司就我一个前端),忙不过 ...
- JS仿QQ空间鼠标停在长图片时候图片自动上下滚动效果
JS仿QQ空间鼠标停在长图片时候图片自动上下滚动效果 今天是2014年第一篇博客是关于类似于我们的qq空间长图片展示效果,因为一张很长的图片不可能全部把他展示出来,所以外层用了一个容器给他一个高度,超 ...
- 寻找与网页内容相关的图片(三)网易新闻与qq空间的做法
寻找与网页相关的图片现在看来无非有两种方式,第一种是网页自己指定,第二种是通过算法推断. 对于网站的内容提供者来说,他自己知道相关的图片在哪,正如前文所述可以在HTML的头部加上META标签,也可以像 ...
- Android 调用系统分享文字、图片、文件,可直达微信、朋友圈、QQ、QQ空间、微博
原文:Android 调用系统分享文字.图片.文件,可直达微信.朋友圈.QQ.QQ空间.微博 兼容SDK 18以上的系统,直接调用系统分享功能,分享文本.图片.文件到第三方APP,如:微信.QQ.微博 ...
随机推荐
- uva340 数字匹配检索问题
这道题目大意是:给定一个secret code,然后输入guess code,让你编程给出提示,提示的格式是(i,j),其中i表示strong match的个数,j表示weak match的个数.所谓 ...
- 步步为营Hibernate全攻略(一)构建Hibernate框架环境
任何一项新技术的出现都有它的必然性,Hibernate也不例外,所以在掌握Hibernate的具体应用之前我们一定先要了解Hibernate是什么?使用Hibernate会给我们的程序开发带来哪些好处 ...
- cocos2d-x 2.1.4学习笔记01:windows平台搭建cocos2d-x开发环境
cocos2d-x的大致开发流程是,首先使用win32版进行代码编写并完成游戏,然后将代码迁移到对应的开发环境上进行交叉编译完成游戏打包,如iphone上是mac+xcode,android是ecli ...
- Find Security Bugs研究,邀请志同道合者一起参与
Find Security Bugs研究,邀请志同道合者一起参与http://automationqa.com/forum.php?mod=viewthread&tid=2803&fr ...
- 集合练习——List部分
利用ArrayList 1.存储多个员工信息,包括工号,姓名,年龄,入职时间,逐条打印所有员工姓名,并输出员工个数. package CollectionPart; import java.util. ...
- Clean Code 笔记 (一):命名
一.命名规范1.名副其实,通过名称我们知道它为什么存在,它做什么事,该怎么用2.避免误导,避免使用与本意相悖的词,提防使用不同之处较小的名称3.有意义的区分,去掉多余的废话比如:a,the.缺少明确约 ...
- 清除Eclipse和Myeclipse中的工作空间目录
打开Eclipse或者Myeclipse后,选择功能菜单里的Windows->Preferences->, 弹出对话框后,选择General->Startup and Shutdow ...
- 自定义标签 tld
初学者可能在不借助IDE工具的情况下 编写自定义标签库 tld 程序可能遇到找不到class 的错误,下面讲解一下如何解决该问题 步骤一:新建一个自定义标签类 HelloWorldTag,该类放到s ...
- 从入行到现在(.net)
每次写东西都不知道怎么去开头.因为一想到要写东西.脑子里面浮现出来的开头就太多. 进园子很久从开始只是关注别人讲的技术,别人讲的基础,到现在更多的去看大家的随笔和新闻.这两年我从零基础的外行人逐渐进入 ...
- MongoDB 2.6.x 的安装部署
1. 下载mongodb 2.6.x版本的zip包,在D盘创建目录MongoDB,解压缩到D:\MongoDB目录. 创建数据库目录D:\MongoDB\data,接下来打开命令行窗口,切换到D:\M ...