【Lua教程】文件上传

一般情况下我们在HTML中使用POST提交数据的时候,一般POST表单代码如下:

<form method="post"action=# >
         <input type="text" name="txt1" value="txt1"></br>
         <input type="text" name="txt2" value="txt2"></br>
         <input type="submit" value="Submit"">
</form>

可以看到POST请求如下:

POST /test.php HTTP/1.1
Host: 172.16.100.168
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://172.16.100.168/test.php
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 19

txt1=txt1&txt2=txt2

其中Content-Type为application/x-www-form-urlencoded。这意味着消息内容会经过URL编码。
获取到POST内容的lua代码如下:

ngx.req.read_body()
for k,v in pairs(ngx.req.get_post_args()) do
    ngx.say(k..":"..v)
end

默认情况下不会读取POST的内容,需要ngx.req.read_body()来开启。也可以在nginx配置中直接使用lua_need_request_body开启。
另外如果提交的数据Content-length大于client_body_buffer_size的设置,那么req.get_post_args()会获取不到数据。

最早的HTTP POST是不支持文件上传的,后来Content-Type的类型扩充了multipart/form-data用以支持向服务器发送二进制数据。
其实form表单在你不写enctype属性时,也默认为其添加了enctype属性值,默认值是enctype=”application/x- www-form-urlencoded”.
我们修改表单的内容如下:

<form method="post"action=# enctype="multipart/form-data">
         <input type="text" name="txt1" value="txt1"></br>
         <input type="text" name="txt2" value="txt2"></br>
         <input type="submit" value="Submit"">
</form>
<?php
echo $_POST['txt1'];
echo $_POST['txt2']
?>

POST请求数据包如下:

POST /test.php HTTP/1.1
Host: 172.16.100.168
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://172.16.100.168/test.php
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------285751406325668
Content-Length: 246

-----------------------------285751406325668
Content-Disposition: form-data; name="txt1"

txt1
-----------------------------285751406325668
Content-Disposition: form-data; name="txt2"

txt2
-----------------------------285751406325668--

可以看到Content-Type变为multipart/form-data; 里面的内容是不经过URL编码的。
读取文件内容的代码如下:
ngx.req.read_body()
ngx.say(ngx.req.get_body_data())
然后返回结果如图:

123

ngx.req.get_body_data()
存在两种情况是会将body内容写入到文件中导致获取不到
1)body体大于设置的client_body_buffer_size(在32位系统中默认为8K,在64位系统中默认为16K)
2)client_body_in_file_only打开
例如上传一个800k的图片,打印的内容就是nil。
然后我们修改lua代码如下:

ngx.req.read_body()
if ngx.req.get_body_data() ~= nil then
    ngx.say(ngx.req.get_body_data())
else
    ngx.say(ngx.req.get_body_file())
end

然后上传800K的图片,返回结果如图:
123

返回了临时文件的图片路径。
然后我们再修改一下lua代码,读取临时文件的内容

ngx.req.read_body()
if ngx.req.get_body_data() ~= nil then
    ngx.say(ngx.req.get_body_data())
else
    RulePath = ngx.req.get_body_file()
    ngx.say(RulePath)
    file = io.open(RulePath,"r")
    if file==nil then
        return
    end
    file:seek("set")--设置从文件头开始读取
    --ngx.say(file:read("*a"))--从当前位置读取整个文件
    ngx.say(file:read(1000))--从当前位置读取1000个字节
    file:close()
end

这样就可以读取到了POST的内容。