Tag Archives: nginx

让 php 用 nginx 打包 zip

php 本身有 zip 模块,可以生产 zip 文件。但是这个 zip 模块只能使用本地文件来打包。如果需要打包输出的文件来自网络,就得先保存临时文件。在文件数量多或者文件大的时候就很杯具。另外,由 php 来输出大的打包文件会占用 php 进程大量时间,影响并发能力。

nginx 有一个第三方模块,mod_zip 。同样可以输出 zip 包。和 X-Accel-Redirect 有点类似,只需要 php 输出相应文件的路径等信息,然后给一个特殊的响应头即可。

nginx zip 模块使用的响应头是 X-Archive-Files: zip 。加上这个响应头,nginx zip 模块就会处理响应正文,完成打包输出。

比如:

printf("%s %d %s %s\n", $crc32, $size, $url, $path );

逐条输出要打包的文件。

$crc32 是 16 进制的文件 crc32 值。也可以不提供,用 “-” 代替。不过这样就没法用 Range 分块下载,断点续传了。
$size 是文件大小的十进制整数。
$url 是要打包的源地址。如果要打包一个本地文件,可以先在 nginx 中做一个 internal path。
$path 是 zip 包中的路径。

不过这样没法创建空目录。一方面,zip 格式开始就没有定义空目录,后来的标准和软件都是通过加一个 / 结尾的 0 大小文件来实现的。这时,就需要先在 nginx 中做一个 internal 的 0 大小文件,比如位于 /_0 。然后输出

printf("%s %d %s %s\n", '00000000', 0, '/_0', $path.'/');

如果要支持中文路径,可以使用 X-Archive-Charset: utf8 这样的响应头,内容为你输出的编码。nginx zip 模块会按标准转换成 utf8 的标准格式。不过各个软件对这个 zip 的标准支持不一,比如 windows 的 zip 目录就不支持,只能以 gbk 编码直接输出。其他软件对编码支持效果也各不相同。测试过的 winrar,7zip,windows zip 目录中,winrar 倒是都可以很好支持。7zip 可能会把部分中文空目录变成 0 大小文件。所以,这点还需要自己斟酌处理。

blog 迁移到这里

原先在 blogbus ,现在搬到这个独立 blog 。

文章评论都用 wget 统统爬下来,再转成了 wp 的导入格式导了进来。但是还有文章里引用的图片。手动保存重新上传实在是太费时间,而且程序员最讨厌机械劳动,就想办法用机器来搞。先用一个 wp 插件 WP RegEx Replace ,把图片地址替换一下,比如 http://photo([0-9]).bababian.com/ 替换为/photo$1.bababian.com/ 。然后用 nginx 的proxy_store ,代理回来存放起来。

location ~ ^/photo[0-9]\.bababian\.com {
if ($request_uri ~ “^/(photo[0-9]\.bababian\.com)/(.+)” ) {
set $bbb_host $1;
set $bbb_uri $2;
}
proxy_store $document_root/$bbb_host/$bbb_uri;
proxy_set_header Referer http://www.bababian.com; #过防盗链
if (!-f $request_filename) {
proxy_pass http://$bbb_host/$bbb_uri;
add_header X-GO-BBB 1; #验证之后的请求使用了保存后的文件
}
}

 

nginx 的 mogilefs 模块

nginx 有一个第三方的 mogilefs 模块,实现了一个 mogilefs 的客户端,可以用 webdav 的方式访问。

文档和下载链接见:http://www.grid.net.ru/nginx/mogilefs.en.html (需翻墙)

用上这个模块,再结合 rewrite,只要 key 在 url 里有,就能直接代理到后端的 mogilefs。

server {
    server_name  download.sample.com;
    charset utf-8;
    access_log  logs/download.access.log  main;
    root   /srv/www; 

    mogilefs_tracker 192.168.1.100:6001;
    mogilefs_domain domain_name;
    mogilefs_class class_name;
    mogilefs_methods GET;

    location ~ ^/files/(.+)  {
        mogilefs_pass $1 {
            proxy_pass $mogilefs_path; 
            proxy_hide_header Content-Type;
        }
    }
    include    extra/mime.types;
}

借用 if 先把 key 放到一变量里,然后就可以传递给后端了。

在 mogilefs_pass 里也可以配置缓存。

写操作配好后,也可以直接用 webdav 方式操作,不用手动向 tracker 请求存储节点,存进去,再通知同步了。

经实验,这个模块表现并不稳定,建议目前不要在生产环境使用。

让 nginx 用上 cronolog

nginx 的日志是不支持 |/path/to/cronolog 这样的写法的,不能直接支持 cronolog 来轮转日志。但是日志总是要拆分的。一般都是每天跑一个 crontab ,把原来的日志 mv 一下,然后 kill -USR1 让 nginx 重新打开日志文件。

以前想到过应该可以利用命名管道来做这事情,昨天去实践了一下。

先创建一个命名管道,

mkfifo /path/to/nginx/logs/access_log_pipe ,当然也可以直接用命名管道代替原来的日志文件。

然后让 cronolog 从这个文件读:

/path/to/cronolog /path/to/log/access_%Y%m%.log <  /path/to/nginx/logs/access_log_pipe &

把 nginx 的日志路径指到那个命名管道然后重启一下就可以了。之后在 nginx 的启动脚本里要把启动 cronolog 放在启动nginx 之前。

 

nginx 的 X-Sendfile —— X-Accel-Redirect

lighttpd 有一个 X-Sendfile 的特性很有意思。比如传统的做一些需要严格验证的下载之类的功能比如收费下载,需要在程序里验证权限,然后由程序读取文件输出,这样性能不好,占用资源也大,而 web server 本身的功能又不足以提供验证。使用 X-Sendfile 就可以让程序来做验证,而把文件传输交给 web server 来做,各自做各自擅长的事情。

本来以为这功能目前就 lighttpd 有,今天发现原来 nginx 也有这能力,apache 也可以通过第三方模块来实现。

nginx 上这个功能叫做 X-Accel-Redirect 。

假设下载文件的路径在 /path/to/files,比如有 /path/to/files/test1.txt 可以在 nginx 里配置

location /down {
     internal;
     alias   /path/to/files;
}

internal 选项是这个路径只能在 nginx 内部访问。

然后可以在 php 里写

header(“X-Accel-Redirect: /down/test1.txt”);

就可以了。

另外,如果在程序那头如果不想要开头的那个“/”,比如想写成 header(“X-Accel-Redirect: down/test1.txt”); ,那么在 nginx 的那条 alias 的最后就要加一个 “/”。

参考:nginx 的相关文档 另一篇英文文章