nginx lua上传文件

3 篇文章 0 订阅
2 篇文章 0 订阅

为了方便上传文件到嵌入式设备,特地借用嵌入式原有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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值