Hello!
2012/7/22 杜威 wrote:
>
> 场景:一个项目中,需要用到nginx的subrequest,把图片发送到亚马逊服务器,处理完成后返回结果到nginx,再进行其他逻辑处理,最后输出结果。
>
> 方案与问题:
> 1、nginx_lua 中的 ngx.location.capture
> 最合适,但发现ngx.location.capture并不支持multipart/form-data
>
由于你没有交待请求体数据的来源,所以我们首先假设请求体来自主请求。考虑下面这个最小化的例子:
location = /t {
content_by_lua '
ngx.req.read_body()
local res = ngx.location.capture("/proxy",
{ method = ngx.HTTP_POST })
ngx.status = res.status
ngx.print(res.body)
';
}
location = /proxy {
proxy_pass http://127.0.0.1:$server_port/echoback;
}
location = /echoback {
echo_read_request_body;
echo $echo_client_request_headers;
echo_request_body;
}
这里我们引入了 location /echoback,利用 ngx_echo 模块回显原始的请求内容(当然,这里也可以借助于 nc
这样的外部工具)。假设主入口在 location /t,它发起一个 POST 子请求到 locaton /proxy,然后 location
/proxy 利用 ngx_proxy 模块发起 HTTP 请求到当前 nginx 服务器的 /echoback 接口。
为方便起见,我们可以使用 curl 工具来发起 multipart 格式的 POST 请求(当然,也完全可以使用其他 HTTP 客户端,包括
Firefox 和 IE 在内)。假设我们需要上传的文件叫做 a.txt(当然,也可以是图片文件),其内容为
$ cat a.txt
hello world
然后发起 POST 请求到 /t 接口:
$ curl -F 'file=@a.txt' localhost:1984/t
POST /echoback HTTP/1.0
Host: 127.0.0.1:1984
Connection: close
User-Agent: curl/7.24.0 (x86_64-redhat-linux-gnu) libcurl/7.24.0
Accept: */*
Content-Length: 195
Content-Type: multipart/form-data;
boundary=----------------------------cdaba37b3cef
------------------------------cdaba37b3cef
Content-Disposition: form-data; name="file"; filename="a.txt"
Content-Type: text/plain
hello world
------------------------------cdaba37b3cef--
我们可以看到,
1. 对于 ngx.location.capture
发起的子请求,默认是会继承父请求的请求头和请求体的,但不会继承请求方法,所以这里我们显式指定了 method = ngx.HTTP_POST
选项。
2. 主请求在发起子请求之前需要自己通过 ngx.req.read_body()
这样的调用首先读取请求体,这样子请求就可以直接继承之。如果依赖于子请求去读取主请求的请求体,可能会出现超时等问题。
如果请求体数据和请求头并不是来自主请求,则需要自己在 Lua 中为子请求构造 multipart/form-data 请求所要求的
Content-Type 请求头和 multipart 格式的请求体,例如:
ngx.req.set_header("Content-Type",
"multipart/form-data;
boundary=----------------------------cdaba37b3cef")
local res = ngx.location.capture("/proxy",
{ method = ngx.HTTP_POST, body = body_data })
> 2、echo_subrequest -f @/filename 也可以,但是会直接输出返回值,导致nginx没办法拿到返回值进行下一步的逻辑处理
>
是的,这里应当使用 ngx_lua 的 ngx.location.capture().
> 3、lua-resty-http 好像也不支持multipart/form-data,文档中也没有提及
>
给 lua-resty-http 库添加 multipart/form-data 支持,应当是很容易的。特别地,如果需要在 ngx_lua
层面对 multipart 上传数据进行分析,也可以参考 lua-resty-upload 这个库:
https://github.com/agentzh/lua-resty-upload
Best regards,
-agentzh
P.S. 已抄送 openresty 邮件组:https://groups.google.com/group/openresty