为了方便上传文件到嵌入式设备,特地借用嵌入式原有nginx环境,添加lua文件上传功能。
ps:因为lua轻量体积小可控,所以选lua喽。
代码如下:
upload.lua
-- Copyright (C) Yichun Zhang (agentzh)
-- local sub = string.sub
local req_socket = ngx.req.socket
local match = string.match
local setmetatable = setmetatable
local type = type
local ngx_var = ngx.var
-- local print = print
local _M = { _VERSION = '0.10' }
local CHUNK_SIZE = 4096
local MAX_LINE_SIZE = 512
local STATE_BEGIN = 1
local STATE_READING_HEADER = 2
local STATE_READING_BODY = 3
local STATE_EOF = 4
local mt = { __index = _M }
local state_handlers
local function get_boundary()
local header = ngx_var.content_type
if not header then
return nil
end
if type(header) == "table" then
header = header[1]
end
local m = match(header, ";%s*boundary=\"([^\"]+)\"")
if m then
return m
end
return match(header, ";%s*boundary=([^\",;]+)")
end
function _M.new(self, chunk_size, max_line_size)
local boundary = get_boundary()
-- print("boundary: ", boundary)
if not boundary then
return nil, "no boundary defined in Content-Type"
end
-- print('boundary: "', boundary, '"')
local sock, err = req_socket()
if not sock then
return nil, err
end
local read2boundary, err = sock:receiveuntil("--" .. boundary)
if not read2boundary then
return nil, err
end
local read_line, err = sock:receiveuntil("\r\n")
if not read_line then
return nil, err
end
return setmetatable({
sock = sock,
size = chunk_size or CHUNK_SIZE,
line_size = max_line_size or MAX_LINE_SIZE,
read2boundary = read2boundary,
read_line = read_line,
boundary = boundary,
state = STATE_BEGIN
}, mt)
end
function _M.set_timeout(self, timeout)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
return sock:settimeout(timeout)
end
local function discard_line(self)
local read_line = self.read_line
local line, err = read_line(self.line_size)
if not line then
return nil, err
end
local dummy, err = read_line(1)
if dummy then
return nil, "line too long: " .. line .. dummy .. "..."
end
if err then
return nil, err
end
return 1
end
local function discard_rest(self)
local sock = self.sock
local size = self.size
while true do
local dummy, err = sock:receive(size)
if err and err ~= 'closed' then
return nil, err
end
if not dummy then
return 1
end
end
end
local function read_body_part(self)
local read2boundary = self.read2boundary
local chunk, err = read2boundary(self.size)
if err then
return nil, nil, err
end
if not chunk then
local sock = self.sock
local data = sock:receive(2)
if data == "--" then
local ok, err = discard_rest(self)
if not ok then
return nil, nil, err
end
self.state = STATE_EOF
return "part_end"
end
if data ~= "\r\n" then
local ok, err = discard_line(self)
if not ok then
return nil, nil, err
end
end
self.state = STATE_READING_HEADER
return "part_end"
end
return "body", chunk
end
local function read_header(self)
local read_line = self.read_line
local line, err = read_line(self.line_size)
if err then
return nil, nil, err
end
local dummy, err = read_line(1)
if dummy then
return nil, nil, "line too long: " .. line .. dummy .. "..."
end
if err then
return nil, nil, err
end
-- print("read line: ", line)
if line == "" then
-- after the last header
self.state = STATE_READING_BODY
return read_body_part(self)
end
local key, value = match(line, "([^: \t]+)%s*:%s*(.+)")
if not key then
return 'header', line
end
return 'header', {key, value, line}
end
local function eof()
return "eof", nil
end
function _M.read(self)
-- local size = self.size
local handler = state_handlers[self.state]
if handler then
return handler(self)
end
return nil, nil, "bad state: " .. self.state
end
local function read_preamble(self)
local sock = self.sock
if not sock then
return nil, nil, "not initialized"
end
local size = self.size
local read2boundary = self.read2boundary
while true do
local preamble = read2boundary(size)
if not preamble then
break
end
-- discard the preamble data chunk
-- print("read preamble: ", preamble)
end
local ok, err = discard_line(self)
if not ok then
return nil, nil, err
end
local read2boundary, err = sock:receiveuntil("\r\n--" .. self.boundary)
if not read2boundary then
return nil, nil, err
end
self.read2boundary = read2boundary
self.state = STATE_READING_HEADER
return read_header(self)
end
state_handlers = {
read_preamble,
read_header,
read_body_part,
eof
}
return _M
Download.lua
local upload = require "./upload"
local UPLOAD_ROOT_PATH = "/mnt/sdcard/upload/"
--local UPLOAD_ROOT_PATH = "/data/upload/"
function split(s, delim)
if type(delim) ~= "string" or string.len(delim) <= 0 then
return
end
local start = 1
local t = {}
while true do
local pos = string.find (s, delim, start, true) -- plain find
if not pos then
break
end
table.insert (t, string.sub (s, start, pos - 1))
start = pos + string.len (delim)
end
table.insert (t, string.sub (s, start))
return t
end
function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
function get_filename(res)
local filename = ngx.re.match(res,'(.+)filename="(.+)"(.*)')
if filename then
return filename[2]
end
end
function DownLoad()
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
form:set_timeout(100000)
while true do
local typ,res,err=form:read()
if not typ then
ErrorMsg="failed to read :"..err
return 1
end
if typ =="header" then
local key=res[1]
local value=res[2]
if key =="Content-Disposition" then
local kvlist= split(value,';')
for _, kv in ipairs(kvlist) do
local seg = trim(kv)
if seg:find("filename") then
local kvfile = split(seg, "=")
filename = string.sub(kvfile[2],2,-2);
if filename then
local linuxTime=tostring(os.time())
filePath=UPLOAD_ROOT_PATH .."/" ..linuxTime.."_"..filename
fileToSave,errmsg = io.open(filePath, "w+")
--存储的文件路径
if not fileToSave then
ErrorMsg="failed to open file "..filePath .. errmsg
return 2
end
else
ErrorMsg="filename cann't find"
return 3
end
--跳出循环
break
end
end
end
elseif typ =="body" then
if fileToSave then
fileToSave:write(res)
end
elseif typ =="part_end" then
if fileToSave then
fileToSave:close()
fileToSave = nil
end
elseif typ =="eof" then
break;
else
ngx.log(ngx.INFO, "do other things")
end
end
return 0
end
local ret = DownLoad();
if ret == 200 or ret == 0 then
ngx.header['Content-Type'] = 'application/json; charset=utf-8'
ngx.say("{code:success}");
else
ngx.header['Content-Type'] = 'application/json; charset=utf-8'
ngx.say("{code:" .. ret .. "}");
end
upload.html
<!DOCTYPE html>
<html>
<head>
<title>File upload example</title>
</head>
<body>
<form action="/Download" method="post" enctype="multipart/form-data">
<input type="file" name="testFileName"/>
<input type="submit" name="upload" value="Upload" />
</form>
</body>
</html>
lua.conf
server {
listen 8080;
client_max_body_size 100m;
client_body_buffer_size 512k;
server_name localhost;
location / {
root /mnt/sdcard/;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
root /mnt/sdcard/lua/;
location /Download
{
content_by_lua_file $document_root/Download.lua;
}
}
nginx编译参数
#luajit
tar -xzvf LuaJIT-2.0.4.tar.gz
cd LuaJIT-2.0.4
make PREFIX=/usr/local/luajit
sudo make install PREFIX=/usr/local/luajit
make clean
mv /usr/local/luajit/lib/libluajit-5.1.so /usr/local/luajit/lib/libluajit-5.1.so.bak
cd ..
rm -rf LuaJIT-2.0.4
#nginx
tar -xzvf nginx-1.10.2.tar.gz
cd nginx-1.10.2
export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0
./configure --prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_v2_module \
--with-ipv6 \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_flv_module \
--with-pcre-jit \
--add-module=/data/source/nginx-rtmp-module \
--add-module=/data/source/lua-nginx-module \
--add-module=/data/source/echo-nginx-module \
--user=www --group=www
make
sudo make install
make clean
cd ..
rm -rf nginx-1.10.2
luajit跨平台编译脚本参考
#luajit
wget http://luajit.org/download/LuaJIT-2.0.4.tar.gz
#luajit
tar -xzvf LuaJIT-2.0.4.tar.gz
cd LuaJIT-2.0.4
make clean
#PREFIX=$HTTP_DIR/install/luajit/
#PREFIX=/datadisk/ee/ac/20170503_product/misc/app/http+rtsp/http/install/luajit/
make PREFIX=/datadisk/ee/ac/20170503_product/misc/app/http+rtsp/http/install/luajit/ HOST_CC="gcc -m32" \
CROSS=/datadisk/ee/ac/20170503_product/rk1108/prebuilts/toolschain/usr/bin/arm-linux-
make PREFIX=/datadisk/ee/ac/20170503_product/misc/app/http+rtsp/http/install/luajit/ HOST_CC="gcc -m32" install
make clean
#mv /usr/local/luajit/lib/libluajit-5.1.so /usr/local/luajit/lib/libluajit-5.1.so.bak
cd ..
rm -rf LuaJIT-2.0.4