PHP使用HTTP Header Range实现断点续传

HTTP Header Range用途:一般是用在断点续传的时候。但是实际的用途很广,例如你的服务器需要输出一个大文件,那么可以用range分段输出,缓解压力。同时在提供音乐视频等服务时可以缓冲下载,如果用户中途关闭,可以节约网络带宽。

<?php
$filename=$_GET['filename'];
$location='media/'.$filename;

$extension = substr(strrchr($filename,'.'),1);

if ($extension == "mp3") 
{
    $mimeType = "audio/mpeg";
} 
else if ($extension == "ogg") 
{
    $mimeType = "audio/ogg";
}

if (!file_exists($location))
{
    header ("HTTP/1.1 404 Not Found");
    return;
}

$size  = filesize($location);
$time  = date('r', filemtime($location));

$fm = @fopen($location, 'rb');

if (!$fm)
{
    header ("HTTP/1.1 505 Internal server error");
    return;
}

$begin  = 0;
$end  = $size - 1;

if (isset($_SERVER['HTTP_RANGE']))
{
    if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches))
    {
        $begin  = intval($matches[1]);
        if (!emptyempty($matches[2]))
        {
          $end  = intval($matches[2]);
        }
    }
}

if (isset($_SERVER['HTTP_RANGE']))
{
    header('HTTP/1.1 206 Partial Content');
}
else
{
    header('HTTP/1.1 200 OK');
}

header("Content-Type: $mimeType");
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');  
header('Accept-Ranges: bytes');
header('Content-Length:' . (($end - $begin) + 1));
if (isset($_SERVER['HTTP_RANGE']))
{
    header("Content-Range: bytes $begin-$end/$size");
}
header("Content-Disposition: inline; filename=$filename");
header("Content-Transfer-Encoding: binary");
header("Last-Modified: $time");

$cur  = $begin;
fseek($fm, $begin, 0);

while(!feof($fm) && $cur <= $end && (connection_status() == 0))
{
    print fread($fm, min(1024 * 16, ($end - $cur) + 1));
    $cur += 1024 * 16;
}

?>

参考:

HTTP Header详解

HTTP/1.1: Header Field Definitions

 

Linux服务器防止暴力破解攻击

最近发现服务器/var/log/auth.log文件里每天无数个暴力SSH登录尝试,各种invalid user。用iptables封掉了几个来源IP,每天还是有新IP来攻击。虽说服务器用key登录,每天暴增的log还是让人十分不爽。网上学习了学习,总结一下Linux服务器防暴力破解攻击(brute-force attacks)的方法:

1. 用复杂一点的用户名/密码

当你把你的ssh暴露在公网下,要想到的第一件事就是黑客们会来猜你的用户名/密码。密码设置复杂一点,这个就不多说了。

2. 禁用root登录

SSH的配置文件在/etc/ssh/sshd_config中。要禁用root登录,确定其中有:

# Prevent root logins:
PermitRootLogin no

然后重启sshd/ssh服务:

service ssh restart

如果你要root权限,用一般用户登录再su。

3. 限制用户登录

仅允许需要远程ssh的用户登录,你可以在sshd_config文件里配置

AllowUsers username1 username2

别忘了重启ssh服务

4. 关闭Protocol 1

SSH可能用到的两个协议:Protocol 1 和 Protocol 2。老的Protocol 1 相较而言安全性要低一些。如果你没有特别的需要,就把它关掉。同样是在sshd_config文件:

# Protocol 2,1
Protocol 2

5. 使用Public/Private Keys来认证

用Keys来认证,一来你不用每次输密码了,二来你可以关闭密码认证,就无所谓那些密码猜测暴力破解了。

6. 使用非标准的端口

SSH默认监听来自22端口的连接,把它改了。(依然是改配置文件,记得重启ssh服务)这个方法没有有些人说的那么有用,但是它至少可以减少你的log里那些只攻击22端口的黑客们留下的记录。其实你不用太担心这些暴力尝试。

不过如果一定要改端口,不要用超过1024的端口。因为在有些Unix-like的系统,低于1024的端口是只有root才有权限监听的。如果使用了超过1024的端口,反而会在某些方面降低服务器的安全性。

更改SSH端口的方法:

修改/etc/ssh/sshd_config文件中:

# Port 22
Port 789

然后重启sshd/ssh服务:

service ssh restart

7. 使用denyhosts或者fail2ban

我搞了个fail2ban。fail2ban可以监视你的系统日志,然后匹配日志的错误信息(正则式匹配)执行相应的屏蔽动作(一般情况下是调用防火墙屏蔽),如:当有人在试探你的SSH、SMTP、FTP密码,只要达到你预设的次数,fail2ban就会调用防火墙屏蔽这个IP,而且可以发送e-mail通知系统管理员,很实用、很强大。

安装fail2ban:

apt-get install fail2ban

安装好以后,目录如下:

/etc/fail2ban/
├── action.d
│   ├── dummy.conf
│   ├── hostsdeny.conf
│   ├── iptables.conf
│   ├── mail-whois.conf
│   ├── mail.conf
│   └── shorewall.conf
├── fail2ban.conf
├── fail2ban.local
├── filter.d
│   ├── apache-auth.conf
│   ├── apache-noscript.conf
│   ├── couriersmtp.conf
│   ├── postfix.conf
│   ├── proftpd.conf
│   ├── qmail.conf
│   ├── sasl.conf
│   ├── sshd.conf
│   └── vsftpd.conf
├── jail.conf
└── jail.local

目录action.d下的文件指定满足条件时执行的一些动作,比如使用iptables禁止ip访问。
目录filter.d下的文件定义匹配日志的正则表达式。
fail2ban.conf文件是配置fail2ban-server程序启动的一些参数。
jail.conf文件包含filter及action的指定。
每个conf文件可被local文件覆盖,conf文件第一个被读取,接着是读取local文件,所以local文件中定义的参数会覆盖conf中的参数。所以我们不需要添加所有的内容到local文件,只需要添加conf文件中你想覆盖的部分参数就好。

配置好以后,启动fail2ban:

service fail2ban start

启动之后,只要符合filter所定义的正则式规则的日志项出现,就会执行相应的action。

Nginx自动生成目录列表

用过Apache的同学都知道Apache可以使用Options Indexes自动生成目录中的文件和子目录列表,Nginx有没有这样的功能呢?答案是:有。

Nginx有个模块ngx_http_autoindex_module,此模块用于自动生成目录列表。当ngx_http_index_module模块未找到索引文件时,ngx_http_autoindex_module便会发出请求来生成目录。

配置实例:

location  /  {
: autoindex  on;
}

说明:

[#autoindex autoindex]
[#autoindex_exact_size autoindex_exact_size]
[#autoindex_localtime autoindex_localtime]

autoindex

syntax: autoindex [ on|off ]
default: autoindex off
context: http, server, location
激活/关闭自动索引

autoindex_exact_size

syntax: autoindex_exact_size [ on|off ]
default: autoindex_exact_size on
context: http, server, location
设定索引时文件大小的单位(B,KB, MB 或 GB)

autoindex_localtime

syntax: autoindex_localtime [ on|off ]
default: autoindex_localtime off
context: http, server, location
开启以本地时间来显示文件时间的功能。默认为关(GMT时间)

在JavaScript中分行书写字符串

在JavaScript中,把字符串分行,有两种书写方式:反斜杠和字符串连接。

字符串连接更利于阅读,但执行较慢,且耗用内存。如果你更关注性能,用反斜杠分行是更好的选择。

1. 反斜杠\分行

var text = "Lorem ipsum dolor sit amet,\
consectetur adipisicing elit,\
sed do eiusmod tempor incididunt\
ut labore et dolore magna aliqua.";

2. +号连接字符串

var text = "Lorem ipsum dolor sit amet,"
         + "consectetur adipisicing elit,"
         + "sed do eiusmod tempor incididunt"
         + "ut labore et dolore magna aliqua.";

在电脑上远程查看和控制Android手机

用电脑控制手机有啥好处呢?首先你可以在你的电脑屏幕上及时收到手机的通知,其次你可以用鼠标和键盘来输入……这比在点点儿大的虚拟键盘上摸来摸去好使多了……还有很多其他的用法,当然有些功能需要你的安卓手机已经取得了root权限。

下面介绍两个方法通过PC来远程查看和控制你的Android设备:

1. 通过Android SDK

你需要:

你的Android设备(手机或平板),你的USB数据线,还有Android SDK开发包(没有的同学点这里下载)。

你还需要下载一个基于Java的小程序:AndroidScreencast(下载)。

如果你电脑里没装Java,还需要安装Java库(下载)。

配置Android SDK

本文以Win7为例,Android SDK同时也有MacOS,Linux上的包,而且我们需要的那个小程序是Java的,所以别的操作系统用此方法也是可以的。

我们首先要配置好Android SDK,如果你不做Android程序开发,那么额外的很多工具包都不需要下载。下载好SDK并解压之后,打开SDK所在的文件夹,瞧那儿有个SDK Manager.exe吧,运行它。然后就出来个这样的窗口:

保留最顶上那个Android SDK Platform-tools,其他的勾都去掉。那些都是做开发用的,如果你只是要远程控制手机,就别下载那么多了,占的空间可不小。

都下载完,就可以把SDK Manager.exe关闭了。

检查电脑和手机之间的连接

接下来要连接电脑和手机了。先进入SDK所在的文件夹,看看那儿有没有一个目录叫作platform-tools。要是没有的话,说明上一步失败了,重新下载SDK Platform-tools吧……

如果有的话,里面应该有个adb.exe文件。运行cmd打开命令行,把adb.exe拖到命令行窗口里去,输入devices作为命令参数,敲回车,你会看到:

如果你的手机和电脑正确连接了,这个命令会把所有连接到电脑的Android设备列出来。

用AndroidScreencast从电脑上连接你的手机

把你之前下载的androidscreencast.jnlp拷贝到SDK文件夹内。

双击这个文件,你会看到上面这个Java Loading的Splash。第一次运行可能会时间稍长,请耐心等待一会儿。

如果运行出现错误,就用adb.exe devices看看连接列表中有没有你的Android手机。

运行成功的话你就会在电脑上看到你的Android设备的屏幕内容:

没有root的手机可以查看手机屏幕,使用其他软件截屏,或者使用内置的screencasting tools对屏幕进行录像。

root过的手机可以通过直接点击屏幕或者下方的Home / Menu / Back / Search / Call / End Call等按钮与手机交互。

我连上我的Motorola Milestone2以后发现个问题,在电脑上操作手机似乎手机不认为自己被操作了,过一会就锁屏了。手机锁屏,当然电脑上这个小屏幕也锁了。不知道其他型号的手机有没有这个问题。如果有,可以暂时关闭手机的自动关闭屏幕功能。对了,在电脑上操作手机,手机屏幕也是一直亮着并且同步显示的噢。

下一次你想远程控制手机的时候只需要用数据线连接好手机以后,运行androidscreencast.jnlp就行了。

这个方法对于root过的手机,几乎所有功能都可以在电脑上完成。不过PC上运行这个Android虚拟机,有时实在太慢了。

如果你只是想通过电脑操作手机进行一些基本的系统功能,如收发短信,操作联系人什么的,又不想速度太慢,可以看看下面第二种方法:

 

2. 通过使用Android应用AirDroid

你需要:WiFi,安装了浏览器的电脑,你的Android手机。AirDroid可以在Google Play里下载安装到你的手机。AirDroid下载

安装好AirDroid以后,手机通过WiFi连上与你的电脑同一局域网,在手机上运行,点击Start运行服务,你会看到:

上面是你的手机服务地址,下面是随机生成的管理密码。

在电脑上打开浏览器,输入上面的地址,使用上面的密码登录:

于是就看到了AirDroid的界面:

在这里你可以收发短信,操作联系人,播放删除设置手机铃声,查看通话记录,查看手机上的文件,备份和卸载手机上的App……等基本的操作。虽然不能微信啊,打游戏啊啥的,不过运行速度比前面SDK的方法还是流畅N倍啊……

 

Enjoy yourselves!

 

PHP设置session过期时间

Session在用户关闭浏览器时都会清除,在PHP中也可以通过设置session过期时间来自动清除。两个途径:

1. 在php.ini中设置session.gc_maxlifetime:

; After this number of seconds, stored data will be seen as 'garbage' and
; cleaned up by the garbage collection process.
; http://php.net/session.gc-maxlifetime
session.gc_maxlifetime = 1440

2. 在代码中设置:

ini_set('session.gc_maxlifetime','1440');

开源的PHP电子表格:GelSheet

GelSheet是一个免费且开源的PHP应用,它提供创建基于web的电子表格功能。

它基于AJAX技术,包含服务器端的PHP代码和前端的JavaScript。

GelSheet最初是Feng Office(OpenGoo)的一个模块,后来逐步发展为3个版本:独立应用,WordPress的插件和Feng Office的插件。

 

 

这个应用程序提供类似桌面效果的界面,并可处理多种格式的电子表格文档。支持处理许多中函数公式的应用,并可将内容导出为多种其它格式的文件。

需要:PHP

兼容性:各主流浏览器

网站:http://www.gelsheet.org/

下载:http://sourceforge.net/projects/gelsheet/files/latest

jQuery Cookie Plugin

jquery.cookie是一个轻量级的读、写、清除浏览器cookies的jQuery插件。

整个插件也就几十行,无非是对document.cookie字符串的拼接组装与解析返回,很奇怪为啥jQuery不把cookie的操作给内置了。以下是插件源码:

/*jshint eqnull:true */
/*!
 * jQuery Cookie Plugin v1.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2011, Klaus Hartl
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.opensource.org/licenses/GPL-2.0
 */
(function($, document) {

	var pluses = /\+/g;
	function raw(s) {
		return s;
	}
	function decoded(s) {
		return decodeURIComponent(s.replace(pluses, ' '));
	}

	$.cookie = function(key, value, options) {

		// key and at least value given, set cookie...
		if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value == null)) {
			options = $.extend({}, $.cookie.defaults, options);

			if (value == null) {
				options.expires = -1;
			}

			if (typeof options.expires === 'number') {
				var days = options.expires, t = options.expires = new Date();
				t.setDate(t.getDate() + days);
			}

			value = String(value);

			return (document.cookie = [
				encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path    ? '; path=' + options.path : '',
				options.domain  ? '; domain=' + options.domain : '',
				options.secure  ? '; secure' : ''
			].join(''));
		}

		// key and possibly options given, get cookie...
		options = value || $.cookie.defaults || {};
		var decode = options.raw ? raw : decoded;
		var cookies = document.cookie.split('; ');
		for (var i = 0, parts; (parts = cookies[i] && cookies[i].split('=')); i++) {
			if (decode(parts.shift()) === key) {
				return decode(parts.join('='));
			}
		}
		return null;
	};

	$.cookie.defaults = {};

})(jQuery, document);

版本1.1,应该不会有更新了,给个下载:jquery.cookie.js

Git主页:https://github.com/carhartl/jquery-cookie/

 

中文文档:

1.安装

在网页加载jQuery库的后面包含jquery.cookie插件(如果你没有将这些代码打包在别的文件里)。

<script src="/path/to/jquery.cookie.js"></script>

2.使用

设置一个session cookie:

$.cookie('the_cookie', 'the_value');

设置一个7天后过期的cookie:

$.cookie('the_cookie', 'the_value', { expires: 7 });

设置一个含有过期时间声明,并且整个站点有效的cookie:

$.cookie('the_cookie', 'the_value', { expires: 7, path: '/' });

读取:

$.cookie('the_cookie'); // => 返回"the_value"
$.cookie('the_cookie', { raw: true }); // => "the_value" 读取时不使用url decode解码
$.cookie('not_existing'); // => 不存在的则返回null

通过将cookie的值设为null来删除cookie:

$.cookie('the_cookie', null);

注意:当删除cookie时,如果之前设置cookie的时候指定了path, domain和secure参数,那么删除时必须指定完全一样的上述参数。

3.参数Options的项

Options可以通过设置 $.cookie.defaults 对象的属性来设置为全局的,也可以通过调用 $.cookie() 时为参数options传入一个对象来分别独立设置。每个独立设置的options都会覆盖 $.cookie.defaults 设置的值。

expires: 365

expires定义了cookie的生命周期。它的值可以是一个数字,表示从创建cookie开始多少天以后过期。也可以是一个Date对象。如果不传此参数,这个cookie会被作为session cookie设置。

这里解释一下,cookies有两种类型:

(1) session cookies,这些是临时的cookie,他们保存在浏览器的内存里,关闭浏览器就会被清除。当你重启浏览器回到之前创建了cookie的页面,网站并不能认出你。
(2) persistent cookies,这些cookie文件会一直保存在你的硬盘上,直到你手动删除他们或者浏览器根据他们的过期时间自动删除他们。

path: '/'

path定义cookie有效的范围。默认cookie只在其创建的页面有效(浏览器标准行为),如果你想让域名下的所有页面都有效,设置 path: ‘/’ 。其默认值为创建cookie的页面。

domain: 'example.com'

domain定义cookie有效的域名。其默认值为创建cookie的页面所属的域名。

secure: true

secure如果为true,cookie的传输会需要安全协议(https)。默认为false。

raw: true

默认情况下,cookie的值在设置和读取的时候,会使用encodeURIComponent / decodeURIComponent 来编码和解码。如果要关闭这一行为,可将raw设置为true。默认值是false。

MySQL中将NULL转为0的方法

1. 如果为空返回0

SELECT IFNULL(NULL, 0)

2. 如果为空返回0,否则返回1

SELECT IF(ISNULL(col), 0, 1) as col

===============================

IFNULL

IFNULL(expr1, expr2)

如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2。IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境。

mysql> SELECT IFNULL(1,0);
        -> 1
mysql> SELECT IFNULL(0,10);
        -> 0
mysql> SELECT IFNULL(1/0,10);
        -> 10
mysql> SELECT IFNULL(1/0,'yes');
        -> 'yes'

IF

IF(expr1,expr2,expr3)

如果expr1是TRUE(expr1<>0且expr1<>NULL),那么IF()返回expr2,否则它返回expr3。IF()返回一个数字或字符串值,取决于它被使用的上下文。

mysql> SELECT IF(1>2,2,3);
        -> 3
mysql> SELECT IF(1<2,'yes','no');
        -> 'yes'
mysql> SELECTIF(strcmp('test','test1'),'yes','no');
        -> 'no'

expr1作为整数值被计算,它意味着如果你正在测试浮点或字符串值,你应该使用一个比较操作来做。

mysql> SELECT IF(0.1,1,0);
        -> 0
mysql> SELECT IF(0.1<>0,1,0);
        -> 1

在上面的第一种情况中,IF(0.1)返回0,因为0.1被变换到整数值, 导致测试IF(0)。这可能不是你期望的。在第二种情况中,比较测试原来的浮点值看它是否是非零,比较的结果被用作一个整数。

CASE

CASE value WHEN [compare-value] THEN result [WHEN [compare-value] THEN result ...] [ELSE result] END

返回result,其中value=compare-value。

CASE WHEN [condition] THEN result [WHEN [condition] THEN result ...] [ELSE result] END

如果第一个条件为真,返回result。如果没有匹配的result值,那么结果在ELSE后的result被返回。如果没有ELSE部分,那么NULL被返回。

mysql> SELECT CASE 1 WHEN 1 THEN "one" WHEN 2 THEN "two" ELSE "more" END;
       -> "one"
mysql> SELECT CASE WHEN 1>0 THEN "true" ELSE "false" END;
       -> "true"
mysql> SELECT CASE BINARY "B" when "a" then 1 when "b" then 2 END;
       -> NULL

PHP强制下载文件

一些诸如 jpg,mp3 类型的文件,通常会在客户端浏览器中直接被播放或使用。如果你希望它们强制被下载,可以使用以下代码:

function forceDownload($file)
{ 
    $file_name = $file; 
    $mime = 'application/force-download'; 
    header('Pragma: public');     // required 
    header('Expires: 0');        // no cache 
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
    header('Cache-Control: private',false); 
    header('Content-Type: '.$mime); 
    header('Content-Disposition: attachment; filename="'.basename($file_name).'"'); 
    header('Content-Transfer-Encoding: binary'); 
    header('Connection: close'); 
    readfile($file_name);        // push it out 
    exit(); 
}