我们使用的是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"
不幸的是。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_body
ngx.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
这里则相反,正文是一次性处理的。
在前面的回答中,我指出。
我们可以修改lua-resty-upload来接受一个类似socket的对象 而不是硬编码的对象 然后写一些缓冲区来从迭代器中缓慢地读取字节
这就完成了。我创建了一个新库,名为 自助餐. 它可以用来创建像普通的 ngx_lua
cosocket对象。并非所有的套接字方法都已实现,但现在它已经拥有了所有的 lua-resty-upload
. 它还没有发布,但我将很快发布第一个版本。
我还分叉并修改了 lua-resty-upload
来添加socket参数。我以后会创建PR到上游仓库。
在你的案例中,有一个如何处理数据的例子。https:/github.comun-deflua-buffettreemasterexamplesresty-chunked-formdata。