如果指定了头,Lua会读取分块的请求体。

问题描述 投票:0回答:1

我们使用的是Nginx + Lua,希望支持分块上传。这个 的工作方法,一般来说是可行的。我的问题是,我怎样才能像往常一样处理上传请求--用头、体、eof工作。

                local form, err = upload:new(chunk_size)
                if not form then
                    ngx.log(ngx.ERR, "failed to new upload: ", err)
                    ngx.exit(500)
                end

                form:set_timeout(1000) -- 1 sec

                while true do
                    local typ, res, err = form:read()
                    if not typ then
                        ngx.say("failed to read: ", err)
                        return
                    end

                    ngx.say("read: ", cjson.encode({typ, res}))

                    if typ == "eof" then
                        break
                    end
                end

就在我把上传的头和体分块的时候 -H "Transfer-Encoding: chunked" 用这个 chunk 脚本。

如果是很明显的事情,很抱歉,但经过几天的google搜索,我没有看到任何例子,但我的建议是。

# read headers
ngx.req.get_headers()

#read body:
ngx.req.get_body_data()

我的建议是: form:read() 并在表格数组中迭代,直到 eof. 感谢任何链接,例子。

curl的例子。

curl -X PUT localhost:8080/test -F file=@./myfile -H "Transfer-Encoding: chunked"
rest nginx lua chunked-encoding openresty
1个回答
1
投票

不幸的是。ngx.req.socket (https:/github.comopenrestylua-nginx-module#ngxreqsocket。),它是由 lua-resty-upload 下,目前还没有处理体编码。也就是说,当你从套接字对象中读取时,你将收到请求体的 照样因此,你需要自己进行解码。lua-resty-upload 它不这样做,它希望是一个普通的formdata体,不需要任何额外的编码。请看 https:/github.comopenrestylua-resty-uploadissues32#issuecomment-266301684。 作进一步解释。

正如上面的链接中提到的,你可以使用 ngx.req.read_bodyngx.re.get_body_data 由 "nginx内置的支持分块编码的请求体阅读器 "支持。该 ngx.re.get_body_data 方法返回一个已经解码的body。您可以将body馈送给一些formdata解析器,这些解析器将body作为字节字符串接受,而不是从cosocket中读取(如 lua-resty-upload 做)。) 例如,您可以使用 lua-resty-multipart-parser: https:/github.comagentzhlua-resty-multipart-parser。

有一个很大的缺点--请求体需要一次性读成Lua字符串,也就是说,整个请求体是作为Lua字符串对象存储在内存中的。

理论上,这一点是可以解决的。我们可以修改 lua-resty-upload 来接受一个类似于socket的对象,而不是硬编码的对象(https:/github.comopenrestylua-resty-uploadblobv0.10librestyupload.lua#L60。),并写一些缓冲区,懒得从迭代器中读取字节,并提供类似socket的接口。也许以后我会尝试一下。


这里是两个库的使用例子。它完全是按照你的要求来做的(但是记住,如果请求主体是 chunked-编码)。)

# nginx.conf
http {
    server {
        listen 8888;
        location = /upload {
            content_by_lua_block {
                require('upload').handler()
            }
        }
    }
}
-- upload.lua
local upload = require('resty.upload')
local multipart_parser = require('resty.multipart.parser')

local get_header = function(headers, name)
    local header = headers[name]
    if not header then
        return nil
    end
    if type(header) == 'table' then
        return header[1]
    end
    return header
end

local handler = function()
    -- return 405 if HTTP verb is not POST
    if ngx.req.get_method() ~= 'POST' then
        return ngx.exit(ngx.HTTP_NOT_ALLOWED)
    end
    local headers = ngx.req.get_headers()
    local content_type = get_header(headers, 'content-type')
    -- return 400 if the body is not a formdata
    if not content_type or not string.find(content_type, '^multipart/form%-data') then
        return ngx.exit(ngx.HTTP_BAD_REQUEST)
    end
    local transfer_encoding = get_header(headers, 'transfer-encoding')
    if transfer_encoding == 'chunked' then
        -- parse form using `lua-resty-multipart-parser`
        ngx.say('*** chunked')
        -- read the body, chunked encoding will be decoded by nginx
        ngx.req.read_body()
        local body = ngx.req.get_body_data()
        if not body then
            local filename = ngx.req.get_body_file()
            if not filename then
                return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
            end
            -- WARNING
            -- don't use this code in production, file I/O is blocking,
            -- you are going to block nginx event loop at this point!
            local fd = io.open(filename, 'rb')
            if not fd then
                return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
            end
            body = fd:read('*a')
        end
        local parser = multipart_parser.new(body, content_type)
        while true do
            local part = parser:parse_part()
            if not part then
                break
            end
            ngx.say('>>> ', part)
        end
    else
        -- parse form using `lua-resty-upload` (in a streaming fashion)
        ngx.say('*** not chunked')
        local chunk_size = 8 -- for demo purposes only, use 4096 or 8192
        local form = upload:new(chunk_size)
        while true do
            local typ, res = form:read()
            if typ == 'eof' then
                break
            elseif typ == 'body' then
                ngx.say('>>> ', res)
            end
        end
    end
end

return {
    handler = handler
}
$ curl -X POST localhost:8888/upload -F file='binary file content'                             

*** not chunked
>>> binary f
>>> ile cont
>>> ent

如你所见,正文是逐块读取和处理的。

$ curl -X POST localhost:8888/upload -F file='binary file content' -H transfer-encoding:chunked

*** chunked
>>> binary file content

这里则相反,正文是一次性处理的。


0
投票

在前面的回答中,我指出。

我们可以修改lua-resty-upload来接受一个类似socket的对象 而不是硬编码的对象 然后写一些缓冲区来从迭代器中缓慢地读取字节

这就完成了。我创建了一个新库,名为 自助餐. 它可以用来创建像普通的 ngx_lua cosocket对象。并非所有的套接字方法都已实现,但现在它已经拥有了所有的 lua-resty-upload. 它还没有发布,但我将很快发布第一个版本。

我还分叉并修改了 lua-resty-upload 来添加socket参数。我以后会创建PR到上游仓库。

在你的案例中,有一个如何处理数据的例子。https:/github.comun-deflua-buffettreemasterexamplesresty-chunked-formdata。

© www.soinside.com 2019 - 2024. All rights reserved.