Openresty Lua实现文件上传

有好久没有写过博客了,已经记不清上次发博客是什么时候了。去年到现在经历很多事,尝试了好多新事物,变化也挺多的。一次偶然机会了解到lua这门新语言,自己简单实现文件上传功能。

上传代码

思路: 根据上传URL来命名文件,使用OpenResty resty.upload模块获取上传文件,调用lua原生类库io.open(filename,'w+')刷入硬盘。具体实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
local cjson = require "cjson"
local result = {}
local upload = require "resty.upload"

-- 获取url 文件名
function get_filename(url)
local filename = string.match(url,'.+/([^/]*%.%w+)$')
return filename
end

-- 错误响应
function err(code,description)
result.code = code
result.description = description
ngx.header['Content-Type'] = 'application/json'
return cjson.encode(result)
end

function ok(data)
result.data = data
result.code = 200
ngx.header['Content-Type'] = 'application/json'
return cjson.encode(result)
end

--上传文件
function upload_file()
local chunk_size = 4096
local form , err = upload:new(chunk_size)
if not form then
ngx.log(ngx.ERR,'failed to new upload: ',err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
local file
local filename = get_filename(ngx.var.uri)
while true do
local typ,res,err = form:read()
if not typ then
ngx.say(err(504,'failed to read '.. err))
return
end

if typ == 'header' then
file = io.open('/usr/local/openresty/nginx/html/lua/' .. filename,'w+')
elseif typ == 'body' then
file:write(res)
elseif typ == 'part_end' then
file:close()
file = nil
ngx.say(ok('upload success'))
return
elseif typ == 'eof' then
break
end
end
end

local http_method = ngx.var.request_method
if http_method == 'POST' then
upload_file()
else
ngx.say(err(405,'not supper method upload file'))
end

Nginx配置

1
2
3
4
location /lua {
default_type text/html;
client_max_body_size 10m;
content_by_lua_file upload.lua;

总结

刚开始做这个的时候,以为lua真的很强大,可以代替Java,PHP做web开发语言。结果我深入了解一下,很快就打了自己的脸了。resty.upload只能获取form-data第一个input,无论是文件还是text类型,只会返回这个。如果你想获取到文件和表单中其他参数,那你就要使用ngx.socket.tcp 获取原生TCP,自己手动解析HTTP文本协议,获取文件数据和表达参数。我之前真的看过一位老哥手动去解析TCP文本,获取文件参数,真的强啊!。 原生类库不支持创建文件夹,创建文件要调用系统命令,这样要进程上下文切换,性能消耗特别大,或者就是使用lfs类库,功能很简单。