2012年12月29日星期六

防止图片盗链

    所谓盗链是指服务提供商自己不提供服务的内容,而是通过技术手段直接在自己的网站上向最终用户提供其他服务商的服务内容,以骗取用户的浏览和点击率。受益者不提供资源或提供很少的资源,而真正的服务提供商却得不到任何的收益。像图片、视频等这些比较大的文件都有可能成为被盗的对象。一旦文件被盗,就会增加服务器的流量,影响网站的访问速度。虽然我们不能100%的保护这些文件不被别人盗用,但可以通过 HTTP Referer 进行限制。使用 Apache 可以很方便地进行设置。假设主机域名为 www.example.com,在文件 .htaccess 中的配置如下:
 
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$ [NC]
RewriteCond %{HTTP_REFERER} ! http://www.example.com [NC]
RewriteCond %{HTTP_REFERER} ! http://www.google.cn [NC]
RewriteCond %{HTTP_REFERER} ! http://www.baidu.com [NC]
RewriteRule .*\.(gif|jpg)$ http://www.example.com/default.jpg [R,NC,L]

    该配置说明说下:

  •     RewriteCond %{HTTP_REFERER} !^$ [NC]
    允许 HTTP_REFERER 为空时访问,即允许用户在浏览器地址栏中直接输入图片地址时显示图片。一般来说这个选项是可选的,建议这样设置。如果将 HTTP_REFERER 强制限制不能为空,那么当用户通过代理服务器访问时,可能会导致一些问题。

  •     RewriteCond %{HTTP_REFERER} ! http://www.example.com [NC]
    设置允许访问的 HTTP_REFERER 来源,包括自身站点,以及 Baidu、Google、Yahoo!等

  • RewriteRule .*\.(gif|jpg)$ http://www.example.com/default.jpg [R,NC,L]
    设置图片被盗链时用于替换的图片,如果有图片被盗链,显示的是 default.jpg。注意,默认图片所在的路径最好跟配置文件不在同一目录下,图片越小越好。如果不想设置替换图片,可以使用下面的语句:

RewriteRule .*\.(gif|jpg)$ - [F]

NC、R、L、F 标记解释

NC:不区分大小写
R: 强制重定向
L:立即停止重写操作
F:强制禁止URL

2012年12月25日星期二

用 jQuery 覆盖 alert、confirm 函数的样式

/**
 * 覆盖默认的alert样式
 * @param message 要显示的内容
 * @param title 标题
 */
function alert(messagetitle,width,height{
    var title title || "提示";
    var width width || "300";
    var height height || "180";
    var obj $('div[id=dialog_alert]').size($('#dialog_alert'$('<div/>'{
        id "dialog_alert"
    });
  
    if ($.ui.dialog{
        if (obj.dialog("isOpen"){
            obj.dialog("close");
        }
        obj.html(message).dialog({
            title title,
            width:width,
            height:height,
            close:function(){
                $(this).dialog('destriy');
            }
        });
    else {
        alert(message);
    }
}
/**
 * 使用jquery ui 模拟javascript confirm功能
 
 * @param string
 *            message 内容
 * @param function
 *            callback 点击确认执行的函数
 * @param function
 *            closeCallback 点击取消执行的函数
 * @return boole false
 */
function myConfirm(messagecallbackcloseCallback{
    if (!$.isFunction(callback){
        try{
            console.error('第二个参数请传递一个函数');
        }catch(e){}
    
        return false;
    }
    if (!$('#dialog-follow').length{
        $('body').append('<div id="dialog-follow"></div>"');
    }
    if ($.isFunction($("#dialog-follow").dialog){
        $("#dialog-follow").dialog({
            width 300,
            modal true,
            resizable false,
            title '温馨提示',
            buttons {
                "确定" function({
                    callback();
                    $(this).dialog("close");
                },
                "取消" function({
                    $(this).dialog("close");
                    if ($.isFunction(closeCallback){
                        closeCallback();
                    }
                }
            }
        });
        $("#dialog-follow").html(message);
    else {
        if (confirm(message){
            callback();
        }
    }
    return false;
}

2012年12月19日星期三

PHP 仿百度文库


1.安装最新版本的OpenOffice


关闭 OpenOffice 程序。打开命令行,进入 OpenOffice 的主目录,键入以下命令。
soffice -headless -accept="socket,port=8100;urp;"

2.下载 JDK

配置 JAVA_HOME 环境变量
  在下一步安装的 JODConverter 要用到 Java。

3.下载  JODConverter

JODConverter是一个JavaOpenDocument文件转换器,可以进行许多文件格式的转换。它依赖于OpenOffice.org或者LibreOffice提供的服务来进行转换,它能将Microsoft Office文档(WordExcelPowerPoint)转换为PDF格式。转成PDF 文档。

<?php
exec('java -jar D:/wamp/bin/apache/Apache2.2.17/htdocs/jodconverter-2.2.2/lib/jodconverter-cli-2.2.2.jar D:\wamp\bin\apache\Apache2.2.17\htdocs\a.doc D:\wamp\bin\apache\Apache2.2.17\htdocs\a.pdf',$output);
print_r($output);
?>

4.安装最新版本的 swftools 工具。

SWFTools工具包中的PDF2SWF工具可用来将PDF格式文件转换成SWF格式。

<?php
exec('"D:\Program Files\SWFTools\pdf2swf.exe" "'.$uploaddir.$time.'.pdf" "-s  poly2bitmap&zoom=72&framerate=15&jpegquality=75&pages=1- " "-o" "'.$uploaddir.$time.'.swf" "-T" "9" "-f"');
?>

5.下载 FlexPaper

FlexPaper是一个开源轻量级的文档浏览组件,被设计和比如PDF2SWF库(包含于SWFTools)一起工作,使得在网页上,Adobe Flex以及别的基于Flash的应用程序中显示和交互PDF文件成为可能。即,使得在没有安装PDF阅读器软件的情况下浏览PDF文件成为可能。FlexPaper项目同时提供了Flex库和独立的Flash版本。

2012年12月7日星期五

MySQL 5.5 主从复制概述

    主从复制功能通过在主服务器和从服务器之间切分处理客户查询的负荷,可以得到更好的客户响应时间 SELECT 查询可以发送到从服务器,以降低主服务器的查询处理负荷。修改数据的语句仍然发送到主服务器,以使主、从服务器保持同步。如果非更新查询为主(如 SELECT 查询),该负载均衡策略很有效。

    MySQL 主从复制优点如下:

  • 增长健壮性。主服务器出现问题时,切换到从服务器作为备份。
  • 优化响应时间。不要同时在主从服务器上进行更新,这样可能引起冲突。
  • 在从服务器备份过程中,主服务器继续处理更新。


主从复制工作原理


    主从复制通过 3 个过程实现,其中一个过程发生在主服务器上,另外两个过程发生在从服务器上。具体情况如下:


  1. 主服务器将用户对数据库更新的操作以二进制格式保存到 Binary Log 日志文件中,然后由 Binlog Dump 线程将 Binary Log 日志文件传输给从服务器。
  2. 从服务器通过一个 I/O 线程将主服务器的  Binary Log 日志文件中的更新操作复制到一个叫 Relay Log 的中继日志文件中。
  3. 从服务器通过另一个 SQL 线程将 Relay Log 中继日志文件中的操作依次在本地执行,从而实现主从之间数据的同步。

  主从复制详细过程如图所示

主从复制过程

(1)BinLog Dump 线程

    BinLog Dump 线程运行在主服务器上,主要工作是把 Binary Log 二进制日志文件的数据发送给从服务器。

    使用SHOW PROCESSLIST 语句查看该线程是否正在运行。

(2)I/O 线程

    从服务器执行 START SLAVE 语句后,创建一个 I/O 线程。此线程运行在从服务器上,与主服务器建立连接,然后向主服务器发出更新请求。之后,I/O 线程将主服务器发送的更新操作复制到本地 Relay Log 日志文件中。

    使用 SHOW SLAVE STATUS 语句查看 I/O 线程状态。

 (3)SQL 线程

    SQL 线程运行在从服务器上,主要工作是读取 Relay Log 日志文件中的更新操作,并将这些操作依次执行,从而使主从服务器数据得到同步。

   

主从复制详述

    MySQL 服务器之间的复制是基于二进制日志机制的。主机的数据库实例会把更新和变化的事件写入二进制日志。根据数据库的设置,二进制日志被存储为不同的日志格式。从服务器根据配置从主服务器那里读取二进制日志,然后在本地数据库执行相应的二进制日志事件。

    在复制过程中,主机是被动的。一旦启用了二进制日志,所有的语句都会被记录进里面。每一台从服务器都会接受一份二进制日志副本,从服务器决定二进制日志哪些语句需要被执行;你不应该在主服务器配置只记录某一类的事件。如果你没有指定的话,在主服务上二进制事件在从服务器都会被执行。如果有需要的话,你可以在从服务器上指定那些应用于特殊的数据库或者表事件被执行。

    每一台从服务器都保存有一条二进制日志坐标的记录:文件名和要从主服务器读取和处理的位置。这意味着多台从服务器能同时连接主机和读取同一个二进制日志不同的部分。因为这个过程是由从服务器处理,单台从服务器连接或者断开主服务器的连接都不会影响主服务器的操作。由于每台从服务器都会记住执行到二进制日志的哪个位置,所以即使断开与主服务器的连接,然后再次连接,都会从上次断开的位置读取二进制日志。

    主服务器和从服务器必须配置成唯一的ID。另外,每台从服务器必须配置主服务的主机名、日志名称和读取日志相应的位置。这些详细的配置可以在从服务器上的会话中用 CHANGE MASTER TO  语句来控制。这些配置信息都保存在从服务器的文件 master.info 中。

2012年12月6日星期四

解决 Yii 1.12 中 Session 丢失的问题


    Session 丢失的问题在运用一些框架来开发比较常见。原因是你对框架 Session 的机制不是很了解,它有可能运用了自己的一套机制。

    情况一般是用 header 跳转后才发生的,因为在 header 之前还能够成功打印出来。

    一般 Session 跨页面丢失的问题有以下几种情况:

    1.客户端禁用 Cookie 。因为 Session 默认是基于 Cookie 的,因为找不到相应的 Cookie,所以 Session 会为空。

    2.你在给 Session 赋值时,前面已经调用了 session_write_close() 函数。在原页面虽然能够成功打印,但是并没有成功保存 Session。

    而这2中情况在 Yii 都不是,打开文件 yii\framework\web\auth\CWebUser.php  #700

     Yii::app()->getSession()->regenerateID(true);

    把这个语句注释掉就可以了。它默认调用了PHP 自带的一个函数

     bool session_regenerate_id [bool delete_old_session] )


    按常理来说是不会出现 Session 丢失问题的。这可能和我的配置或者它内部实现 Seesion 的机制有关系。

    你还可以查看PHP 手册 《Session 会话处理函数》 一章,了解更多详情。

2012年11月26日星期一

MySQL 5.5 复制格式

基于语句复制的优点

从 MySQL 3.23 起就已经支持基于语句复制了

不用把大量的数据写进日志文件。当删除或者更新大量的数据时,日志的储存空间增长速度不会很快

日志记录了那些数据更改的SQL语句,保证数据库的一致。

基于语句复制的缺点

  • 基于语句的复制中,以下语句是不安全的。使用基于语句的复制中,并非所有的修改数据(例如 INSERT DELETE, UPDATE和 REPLACE语句)语句都可以成功被复制。在使用基于语句的复制中,任何不确定的行为是难以复制的。诸如这类数据修改语言(Data Modification Language),还有以下这些
  • 在基于行的复制中,对于 INSERT ... SELECT 的 SQL 语句,需要行级别的锁定。
  • 在需要进行表扫描(因为 WHERE 子句中没有用到索引)的UPDATE 语句中,在基于行的复制中必须锁定行
  • 对于使用InnoDB引擎的:一个使用 AUTO_INCREMENT 的 INSERT 语句,会阻塞其他不冲突的 INSERT 语句。
  • 对于复杂的语句,语句必须先被从服务器识别和执行,再进行更新或插入操作。基于行的复制,从服务器只会修改受影响的行,不执行所有语句。
  • 在从服务器上在识别出现错误的话,特别是在执行复杂的语句时,基于语句的复制会随着时间的推移,那些受影响的行可能会慢慢增加误差。
  • 存储函数调用语句时会执行相同的 NOW()值。
  • 用户自定义的函数功能必须是唯一确定的,才能适用于从服务器
  • 在主服务器和从服务器之间,表的定义必须保持一致

基于行复制的优点

  • 所有的改变都能被复制,这在复制中是最安全的一种方式。

mysql 数据库不会被复制, mysql 数据库不会被看作一个节点特定的数据库。但是,基于语句的复制会复制这些信息,包括 GRANT、REVOKE 还有触发器,存储过程和视图,都会被从服务器复制。

对于这些语句 CREATE TABLE ... SELECT,用 CREATE 语句创建表,都是用基于语句的复制的格式。而数据的插入则是使用 行复制。

对于下面的语句,在主服务器上只要很少的行锁定,能支持高并发。

INSERT ... SELECT

带有 AUTO_INCREMENT 的 INSERT 语句


UPDATE or DELETE statements with WHERE clauses that do not use keys or do not change most of the examined rows.


Row-Based 日志和复制的用法



  • RBL,非事务表和停止从服务器。 如果用 row-based 记录时,当从服务器在更新一个非事务表时,从服务器忽然被停止了,从服务器的数据库的可能会处于非一致的状态。所以当用 row-based 记录时,推荐用支持事务的引擎,如 InnoDB。
  • 数据库级别的复制选项。这些 --replicate-do-db, --replicate-ignore-db, 和 --replicate-rewrite-db 选项在基于  row-based 和  statement-based 来记录时,区别是很大的。基于这一点,尽量避免使用数据库级别的选项而改用表级别的选项,诸如  --replicate-do-table 和 --replicate-ignore-table
  • 不支持过滤 server ID 系统变量。经常会遇到这样一个场景:在 UPDATE 或者 DELETE 语句中通过 WHERE 中用 @@server_id <> id_value 语句来过滤掉从服务器的改变。如 WHERE @@server_id <> 1 。这个在用 row-based 来记录时,不能正常的工作。如果你一定要用 server_id 系统变量来声明过滤,要有 --binlog_format=STATEMENT.你也可以在  CHANGE MASTER TO 语句中 用IGNORE_SERVER_IDS 选项来过掉server_id 系统变量的影响。
  • 二进制日志缺乏校验
  • 当系统变量 slave_exec_mode 的值是 IDEMPOTENT 时,未能将更改应用于row-based 记录。因为无法找到原来的行不触发一个错误或会导致复制失败。这意味着更新不能成功的应用于从服务器上。所以主服务器和从服务器不会再同步。

2012年11月5日星期一

CURL 模拟登陆并获取数据

    在做采集程序时,有时我们要抓取一些登录才能访问的页面。但是有时即使我们登录成功了,却还是无法抓取相关页面,这是为什么呢?

    嗯,最有可能的原因是没把登录成功后的 cookie 一并传递过去。

    对于一些安全防范措施不是做得很高的网站,我们可以通过 PHP 的函数 curl_setopt 来登录的。

<?php
//在指定目录中建立一个具有唯一文件名的文件。如果该目录不存在,tempnam() 会在系统临时目录中生成一个文件,并返回其文件名。 
$cookie_file tempnam('./tmp','cookie');//其中 cookie 为文件名的前缀
$postfield 'LoginForm[username]=admin&LoginForm[password]=admin&LoginForm[rememberMe]=0&yt0=Login';
$url "http://localhost/testdrive/index.php?r=site/login";//登录 提交的 url,可以通过 firfox 的 firebug 工具或者 google chrome 的开发人员工具来查看
$ch curl_init($url);
curl_setopt($chCURLOPT_RETURNTRANSFER1);
curl_setopt($chCURLOPT_HEADER0);
curl_setopt($chCURLOPT_POST1);
curl_setopt($chCURLOPT_COOKIEJAR$cookie_file);//保存 cookie 的文件
curl_setopt($chCURLOPT_SSL_VERIFYPEERfalse);
curl_setopt($chCURLOPT_POSTFIELDS,$postfield);
$strlen curl_exec($ch);

$url "http://localhost/testdrive/index.php";//访问登录后的页面。
$ch curl_init($url);
curl_setopt($chCURLOPT_RETURNTRANSFER0);
curl_setopt($chCURLOPT_SSL_VERIFYPEERfalse);
curl_setopt($chCURLOPT_COOKIEJAR$cookie_file);//再次发送请求时,cookie 就会自动传递过去
$strlen curl_exec($ch);
?>