nginx upload module的使用

如今的網站,總會有一點與用戶交互的功能,例如容許用戶上傳頭像,上傳照片,上傳附件這類的。PHP寫的程序,對於上傳文件效率不是很高。
幸虧,nginx有一個名爲upload的module能夠解決這個問題。網絡上已經有不少關於upload module的文章,可是大部分都是介紹編譯安裝這些的,對於一些細節敘述不是很清楚,因而本身寫了這篇。
參考了不少其餘人的文檔,在此致謝,詳細見參考文檔部分。

1、upload module的工做原理
nginx upload module模塊經過nginx服務器來接受用戶上傳的文件,它自動分析客戶端的上傳請求,將上傳的文件保存到 upload_store 所指向的目錄位置。
而後這些文件信息將被從原始的請求中剔除,從新組裝好上傳參數後轉交到後端由 upload_pass 指定的location去處理,這樣後端就能夠任意處理上傳的文件。
每個上傳的 file 字段值將能夠由upload_set_form_field 指定的值替換。
上傳的文件能夠經過$upload_tmp_path 變量訪問到。
上傳的文件通過處理之後,由 upload_cleanup 指定的條件控制刪除清理。

以上表達可能不夠直觀,咱們舉個例子,假如咱們使用以下一個頁面做爲上傳頁面:

[root@Nginx-Mod-DEV wwwroot]# cat index.html
<html>
<head>
<title>Test upload</title>
</head>
<body>
<h2>Select files to upload</h2>
<form name="upload" method="POST" enctype="multipart/form-data" action="/upload">
<input type="file" name="file1"><br>
<input type="file" name="file2"><br>
<input type="submit" name="submit" value="Upload">
<input type="hidden" name="test" value="value">
</form>
</body>
</html>

那麼咱們選擇兩個文件,點擊"Upload"按鈕之後,nginx upload module會接收上傳來的文件,將上傳的文件保存到 upload_store 所指向的目錄位置。
而後上傳的文件信息將被從原始的請求中剔除,根據nginx.conf中的配置從新組裝好上傳參數,而後轉到後端由 upload_pass 指定的位置去處理。
假如咱們上傳得兩個文件分別名爲:Picture 1.png和Picture 2.png,那麼upload module接收文件之後,會傳遞給後端相似下邊這樣的一組參數:

"file1.path"=>"/tmp/0000123458",
"file2.path"=>"/tmp/0000123459",
"file1.content_type"=>"image/png",
"file2.content_type"=>"image/png",
"file1.name"=>"Picture 1.png",
"file2.name"=>"Picture 2.png",

這樣後端的PHP代碼就可使用$_POST變量獲取這些參數來處理上傳進來的文件了。下邊是一個簡單的示例,只是顯示出相應的變量的值,而後根據文件的MD5值,取第一個字符做爲對應的一級目錄名稱,取最後一個字符做爲第二級目錄名稱,而後將上傳的文件移動到相應的目錄中。

<?php
$temppath = $_POST["file1_path"];
$name = $_POST["file1_name"];
$md5 = $_POST["file1_md5"];
$f_dir = substr($md5,0,1);
$s_dir = substr($md5,-1);
$final_file_path = "/".$f_dir."/".$s_dir."/".$name;

echo $temppath."<br />";
echo $name."<br />";
echo $md5."<br />";
echo $f_dir."<br />";
echo $s_dir."<br />";
echo $final_file_path;
rename($temppath,$final_file_path);
?>

由於nginx upload module已經作完了最費時的mime解析工做,後端的PHP代碼只須要簡單的移動文件到合適的位置就能夠了。由於upload module是使用C語言寫的,比起PHP做解析工做的效率高多了,所以極大地提升了文件上傳的效率。

2、upload module的配置參數簡要說明

下邊是一些配置參數的說明:

upload_pass 指明瞭須要後續處理的php地址
upload_cleanup 若是php出現400 404 499 500-505之類的錯誤,則刪除上傳的文件
upload_store 上傳文件存放地址
upload_store_access 上傳文件的訪問權限,user:r是指用戶可讀
upload_limit_rate 上傳限速,若是設置爲0則表示不限制
upload_pass_form_field 從表單原樣轉到後端的參數,能夠正則表達式表示
官方的例子是upload_pass_form_field "^submit$|^description$";
意思是把submit,description這兩個字段也原樣經過upload_pass傳遞到後端php處理。若是但願把全部的表單字段都傳給後端能夠用upload_pass_form_field "^.*$";

upload_set_form_field可使用的幾個變量
    $upload_field_name    表單的name值
    $upload_content_type    上傳文件的類型
    $upload_file_name    客戶端上傳的原始文件名稱
    $upload_tmp_path    文件上傳後保存在服務端的位置

upload_aggregate_form_field 能夠多使用的幾個變量,文件接收完畢後生成的
    $upload_file_md5    文件的MD5校驗值
    $upload_file_md5_uc    大寫字母表示的MD5校驗值
    $upload_file_sha1    文件的SHA1校驗值
    $upload_file_sha1_uc    大寫字母表示的SHA1校驗值
    $upload_file_crc32    16進製表示的文件CRC32值
    $upload_file_size    文件大小

3、編譯安裝的步驟

這個很簡單,也有不少人寫了不錯的文章,在此流水帳記錄一下。
    
1.編譯環境準備---這部分不須要,由於我要用到nginx-lua-module,因此列在這裏了

[ lua 5.1 ]
wget http://www.lua.org/ftp/lua-5.1.5.tar.gz
tar xvzf lua-5.1.5.tar.gz
cd lua-5.1.5/src
make linux
cd ..
make install

此處默認安裝位置是/usr/local,若是安裝到其餘位置須要:
export LUA_LIB=/path/to/lua/lib
export LUA_INC=/path/to/lua/include

另外,lua-ngix-module目前版本與lua 5.2不兼容,不要選擇lua 5.2

[ pcre 8.30 ]
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.30.tar.gz
tar xvzf pcre-8.30.tar.gz

2.編譯nginx

[ chaoslawful-lua-nginx-module ]---這部分不須要
https://github.com/chaoslawful/lua-nginx-module/downloads

[ nginx_upload_module-2.2.0 ]
wget http://www.grid.net.ru/nginx/download/nginx_upload_module-2.2.0.tar.gz
tar xvzf nginx_upload_module-2.2.0.tar.gz

[ nginx 1.0.15 ]
wget http://nginx.org/download/nginx-1.0.15.tar.gz
tar xvzf nginx-1.0.15.tar.gz
cd nginx-1.0.15
./configure --prefix=/usr/local/nginx --with-pcre=../pcre-8.30 --add-module=../nginx_upload_module-2.2.0 --add-module=../chaoslawful-lua-nginx-module-8d28785 --without-mail_pop3_module --without-mail_imap_module  --without-mail_smtp_module --with-http_ssl_module  --with-http_stub_status_module  --with-http_gzip_static_module
make && make install

4、一些常見的問題

疑問1.upload_set_form_field的變量名
http://serverfault.com/questions/152194/merging-variable-with-string-in-config-file

Question:
I the have following setup in my conf file
upload_set_form_field $upload_field_name.name "$upload_file_name";
But I want change chosen param name to:
upload_set_form_field ($upload_field_name+"[name]") "$upload_file_name";
So I can get "attachment[name]" but this doesn't work. I would be very happy if someone could help me with merging variables with string in nginx config file :).

Anwser:
Nginx does not have a concatenation character, rather it's based on valid and invalid characters, for instance in the directive:
try_files $uri $uri/ @fallback;
$uri is the variable and / is a string to append since / cannot be in a variable name.
Similarly you should try
$upload_field_name[name] "$upload_file_name";
If this doesn't work then try.
set $foo [name];
$upload_field_name$foo "$upload_file_name";
I cannot say if the upload module will even allow this, though. Minor syntax errors might also be present.

疑問2.upload模塊不支持輸入數組
http://newbdez33.blogspot.com/2009/05/nginx-upload-module-does-not-support.html

Nginx upload module does not support input array for PHP
Please refer below email,
    Jacky Zhang wrote:
    > Hi Valery,
    >
    > It's fine if I using nginx upload module to upload a signle file.
    > but It look incorrect that when I use a input like this:
    >
    >

    <input type="file" name="userfiles[]" />

    >
    > Do you have any suggestion?

    No, arrays won't work, you have to list every file input field individually.
    --
    Best regards,
    Valery Kholodkov
    
疑問3.如何限制上傳文件的大小

http://wiki.nginx.org/HttpCoreModule#client_max_body_size

php.ini中要設置一下

file_uploads     on     是否容許經過HTTP上傳文件的開關。默認爲ON便是開
upload_tmp_dir     –     文件上傳至服務器上存儲臨時文件的地方,若是沒指定就會用系統默認的臨時文件夾
upload_max_filesize     8m     望文生意,即容許上傳文件大小的最大值。默認爲2M
post_max_size     8m     指經過表單POST給PHP的所能接收的最大值,包括表單裏的全部值。默認爲8M

通常地,設置好上述四個參數後,在網絡正常的狀況下,上傳<=8M的文件是不成問題
但若是要上傳>8M的大致積文件,只設置上述四項還必定能行的通。除非你的網絡真有100M/S的上傳高速,不然你還得繼續設置下面的參數。
max_execution_time     600     每一個PHP頁面運行的最大時間值(秒),默認30秒
max_input_time     600     每一個PHP頁面接收數據所需的最大時間,默認60秒
memory_limit     8m     每一個PHP頁面所吃掉的最大內存,默認8M

另外還要在nginx.conf中作設置。
upload_max_file_size <size>   這個是個軟限制
client_max_body_size 這個是硬限制(默認1m)

附錄: nginx.conf的配置

# Generic startup file.
user nginx;
worker_processes  4;
worker_rlimit_nofile 65536;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

# Keeps the logs free of messages about not being able to bind().
#daemon     off;

events {
        use epoll;
        worker_connections  10240;
}

http {
#       rewrite_log on;

        include mime.types;
        default_type       application/octet-stream;
        access_log         /var/log/nginx/access.log;
        sendfile           on;
#       tcp_nopush         on;
        keepalive_timeout  3;
#       tcp_nodelay        on;

        gzip on;
        gzip_http_version 1.1;
        gzip_comp_level 2;
        gzip_types    text/plain text/css
                      application/x-javascript text/xml
                      application/xml application/xml+rss
                      text/javascript;
        client_max_body_size 13m;
        index index.php index.html index.htm;

        server {
        listen 80;
        server_name image.demo.com;
        access_log /var/log/nginx/image.access.log;
        location / {
        index index.html;
        root  /images;
        }
        }

        server{
        listen 80;
        server_name upload.demo.com;
        root  /var/wwwroot;

        # 上傳頁面會把文件上傳到這個location
        location /upload {
        # 文件上傳之後轉給後端的PHP代碼去處理
        upload_pass /movefile.php;

        # 上傳的文件臨時存儲位置
        # 此處注意,我把臨時文件目錄放到了tmpfs中,爲了速度,但有丟失數據的風險!
        upload_store /dev/shm;
        
        # Allow uploaded files to be read only by user
        upload_store_access user:r;

        # Set specified fields in request body
        upload_set_form_field $upload_field_name.name "$upload_file_name";
        upload_set_form_field $upload_field_name.content_type "$upload_content_type";
        upload_set_form_field $upload_field_name.path "$upload_tmp_path";


        # Inform backend about hash and size of a file
        upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5";
        upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size";

        upload_pass_form_field "^submit$|^description$";

        upload_cleanup 400 404 499 500-505;
        }

#        location @test {
#        proxy_pass   http://127.0.0.1;
#        }

        location ~ \.php {
            fastcgi_pass  127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi.conf;
        }
        }

}

參考文檔:javascript

http://blog.martinfjordvald.com/2010/08/file-uploading-with-php-and-nginx/php

http://brainspl.at/articles/2008/07/20/nginx-upload-modulecss

http://www.phpabc.cn/nginxphp-fpmyou-hua-post-xing-neng.htmlhtml

http://deidara.blog.51cto.com/400447/389873java

http://anerg.com/read.php?55node

http://t.lava.cn/blog.php?id=23726linux

http://blog.joshsoftware.com/2010/10/20/uploading-multiple-files-with-nginx-upload-module-and-upload-progress-bar/nginx

http://matthewhutchinson.net/2010/1/6/nginx-upload-module-with-paperclip-on-railsgit

http://b.oldhu.com/2009/06/09/uploading-multiple-large-files-to-a-rails-application/github

http://www.tutorialchip.com/php-upload-class/

相關文章
相關標籤/搜索