能弄个lua-resty-download吗?

525 views
Skip to first unread message

lhmwzy

unread,
Mar 10, 2012, 8:02:57 PM3/10/12
to open...@googlegroups.com
现在有lua-resty-upload,可以用来上传文件,能实现流式的lua-resty-download吗?比如要下载的文件不WEB目录中,用lua-resty-download去读,然后通过流式分段输出到客户端,以此来实现下载,适合于网盘等场合。
先问问能实现吗?
我水平低,想了想,没想出来如何分段输出。
分段读,然后输出到一个WEB临时文件,然后再让客户下载这个文件,下载完后,删除临时文件,这个思路对?
欢迎大家讨论。

Zoom.Quiet

unread,
Mar 10, 2012, 9:40:51 PM3/10/12
to open...@googlegroups.com

cosocket API 还可以用于读取巨大的请求体数据
http://agentzh.org/misc/slides/ngx-openresty-ecosystem/index.html#59
实话是内置的,,,
- 上传可以流式读取
- 下载自然也可以使用相同机制来吼


--
人生苦短, Pythonic! 冗余不做,日子甭过!备份不做,十恶不赦!
俺: http://about.me/zoom.quiet
文字协议: http://creativecommons.org/licenses/by-sa/2.5/cn/

lhmwzy

unread,
Mar 10, 2012, 9:43:21 PM3/10/12
to open...@googlegroups.com
嗯,老师写个代码出来?

lhmwzy

unread,
Mar 10, 2012, 9:45:23 PM3/10/12
to open...@googlegroups.com
而且,我要求的是从某个文件位置读,文件并不在WEB目录下,用HTTP访问不到


--
邮件自: 列表“openresty”,专用于技术讨论!
发言: 请发邮件到 open...@googlegroups.com
退订: 请发邮件至 openresty+...@googlegroups.com
详情: http://groups.google.com/group/openresty
官网: http://openresty.org/
仓库: https://github.com/agentzh/ngx_openresty
建议: 提问的智慧 http://wiki.woodpecker.org.cn/moin/AskForHelp

wd

unread,
Mar 10, 2012, 10:07:53 PM3/10/12
to open...@googlegroups.com
不是有 csv 之类的输出模块么

2012/3/11 lhmwzy <lhm...@gmail.com>:

lhmwzy

unread,
Mar 10, 2012, 11:11:03 PM3/10/12
to open...@googlegroups.com
csv是干什么的?


在 12-3-11,wd<w...@wdicc.com> 写道:

agentzh

unread,
Mar 11, 2012, 7:04:20 AM3/11/12
to open...@googlegroups.com
如果你的数据源是磁盘文件,可以使用类似下面的代码来分块读取文件内容并流式输出给下游 HTTP 连接:

   local chunk_size = 4096  -- this is just an example
   local file_path = "/tmp/big.txt"  -- this is just an example

   local f, err = io.open(file_path, "r")
   if not f then
       ngx.say("failed to open file: ", err)
       return
   end

   while true do
       local chunk = f:read(chunk_size)
       if not chunk then
           break
       end

       ngx.print(chunk)
       ngx.flush(true)
   end

这个例子中的关键是利用了 ngx.flush 这个接口:

http://wiki.nginx.org/HttpLuaModule#ngx.flush

不过这里需要注意的一个问题是 ngx_lua 对于 HTTP 1.0 是自动对响应体进行全缓存的(为了支持 HTTP 1.0 Keepalive),而目前没有提供配置方式禁止此行为,所以当客户端发起的是 HTTP 1.0 请求,上面的代码并不能实现流式输出。我这两天将实现一个 lua_http10_buffering 配置选项,上面的例子中只要在 nginx.conf 中加上

   lua_http10_buffering off;

就不会再有 HTTP 1.0 全缓存输出的行为了。

如果你需要输出的数据源并不在本机文件系统中,而是某个 TCP 连接,则可以把上例中的分块文件读取操作替换为 cosocket receive 操作。不过,如果后端不够强大,根本受不了慢速 TCP 连接的话(例如许多关系数据库的连接),还是在输出之前先全缓存到本地文件系统的好。

Best regards,
-agentzh

agentzh

unread,
Mar 11, 2012, 7:07:14 AM3/11/12
to open...@googlegroups.com
On Sun, Mar 11, 2012 at 7:04 PM, agentzh <age...@gmail.com> wrote:
如果你的数据源是磁盘文件,可以使用类似下面的代码来分块读取文件内容并流式输出给下游 HTTP 连接:

   local chunk_size = 4096  -- this is just an example
   local file_path = "/tmp/big.txt"  -- this is just an example

   local f, err = io.open(file_path, "r")
   if not f then
       ngx.say("failed to open file: ", err)
       return
   end

   while true do
       local chunk = f:read(chunk_size)
       if not chunk then
           break
       end

       ngx.print(chunk)
       ngx.flush(true)
   end


当然,如果可能的话,还是尽量使用 Nginx 自己的 ngx_static 模块提供的静态文件下载服务。

Best regards,
-agentzh

lhmwzy

unread,
Mar 11, 2012, 7:14:57 AM3/11/12
to open...@googlegroups.com
嗯,是的。我是在想如何利用这个做一个类似于网盘的东西,显然,不能直接提供HTTP下载,要不然,网盘就失去权限控制作用了。
我不太清楚如115、dropbox等网盘是如何实现文件下载的。

Zoom.Quiet

unread,
Mar 11, 2012, 7:27:38 AM3/11/12
to open...@googlegroups.com
在 2012年3月11日 下午7:14,lhmwzy <lhm...@gmail.com> 写道:
> 嗯,是的。我是在想如何利用这个做一个类似于网盘的东西,显然,不能直接提供HTTP下载,要不然,网盘就失去权限控制作用了。
> 我不太清楚如115、dropbox等网盘是如何实现文件下载的。
>
- 对于web 下载,一般使用专用缓存和应用框架的, eg:
Twiggy/Starman 等等PSGI服务

see:c30k和nginx的AIO | N.S.thoughts
http://nightsailer.com/2010/12/16/827.html

agentzh

unread,
Mar 11, 2012, 8:15:52 AM3/11/12
to open...@googlegroups.com
On Sun, Mar 11, 2012 at 7:14 PM, lhmwzy <lhm...@gmail.com> wrote:
嗯,是的。我是在想如何利用这个做一个类似于网盘的东西,显然,不能直接提供HTTP下载,要不然,网盘就失去权限控制作用了。

谁说 ngx_lua 不能和 ngx_static 等其他 nginx 模块一起协同工作了?至少有两种玩法:

1. 使用 access_by_lua 在 access 阶段进行权限控制,而 content 阶段仍由 ngx_static 模块提供静态文件服务。

http://wiki.nginx.org/HttpLuaModule#access_by_lua

关于 nginx 请求处理阶段的概念,可以参见我的 Nginx 教程系列“Nginx 配置指令的执行顺序”:

http://blog.sina.com.cn/s/articlelist_1834459124_2_1.html

2. 在入口 location 中使用 content_by_lua 进行相关处理,但不直接输出响应,最后再用 ngx.exec() 发起内部跳转到一个配置了静态文件服务的 internal location 输出文件内容。

http://wiki.nginx.org/HttpLuaModule#ngx.exec

Regards,
-agentzh

lhmwzy

unread,
Mar 11, 2012, 8:20:27 AM3/11/12
to open...@googlegroups.com
嗯,这个了解,在我的BLOG里也实现了对静态文件html的控制,呵呵



Regards,
-agentzh

Zoom.Quiet

unread,
Mar 11, 2012, 10:07:35 AM3/11/12
to open...@googlegroups.com
在 2012年3月11日 下午8:15,agentzh <age...@gmail.com> 写道:
> On Sun, Mar 11, 2012 at 7:14 PM, lhmwzy <lhm...@gmail.com> wrote:
>>
>> 嗯,是的。我是在想如何利用这个做一个类似于网盘的东西,显然,不能直接提供HTTP下载,要不然,网盘就失去权限控制作用了。
>
>
> 谁说 ngx_lua 不能和 ngx_static 等其他 nginx 模块一起协同工作了?至少有两种玩法:
>

- 总之,根据 nginx 的执行阶段来切分
- 只要是作用于不同阶段的模块,就可以串接起来共同作用
- 好在 opernersty 包含了所有主要阶段的处置
- 而且可以通过内部子请求的方式,分裂出新的执行序列来
- 从而可以引入其它模块的能力
嗯嗯嗯,怎么想都感觉,这里有个要命的调试技巧问题:
- 万一将作用同一阶段的不同模块想当然的写到一齐
- 报錯时,是否明确指出为什么?
或是,已经通过模块的命名本身就强烈的暗示只能使用在什么阶段?

> 1. 使用 access_by_lua 在 access 阶段进行权限控制,而 content 阶段仍由 ngx_static 模块提供静态文件服务。
>
> 见 http://wiki.nginx.org/HttpLuaModule#access_by_lua
>
> 关于 nginx 请求处理阶段的概念,可以参见我的 Nginx 教程系列“Nginx 配置指令的执行顺序”:
>
> http://blog.sina.com.cn/s/articlelist_1834459124_2_1.html
>
> 2. 在入口 location 中使用 content_by_lua 进行相关处理,但不直接输出响应,最后再用 ngx.exec()
> 发起内部跳转到一个配置了静态文件服务的 internal location 输出文件内容。
>
> 见 http://wiki.nginx.org/HttpLuaModule#ngx.exec
>
> Regards,
> -agentzh

agentzh

unread,
Mar 11, 2012, 11:31:45 PM3/11/12
to open...@googlegroups.com
On Sun, Mar 11, 2012 at 10:07 PM, Zoom.Quiet <zoom....@gmail.com> wrote:
- 总之,根据 nginx 的执行阶段来切分
- 只要是作用于不同阶段的模块,就可以串接起来共同作用
- 好在 opernersty 包含了所有主要阶段的处置
 - 而且可以通过内部子请求的方式,分裂出新的执行序列来
 - 从而可以引入其它模块的能力
嗯嗯嗯,怎么想都感觉,这里有个要命的调试技巧问题:
   - 万一将作用同一阶段的不同模块想当然的写到一齐
   - 报錯时,是否明确指出为什么?
或是,已经通过模块的命名本身就强烈的暗示只能使用在什么阶段?


请仔细阅读我的 nginx 教程系列,里面对这些问题都有很具体的讨论:

http://agentzh.org/misc/nginx/agentzh-nginx-tutorials-zhcn.html

Regards,
-agentzh

Zoom.Quiet

unread,
Mar 12, 2012, 12:33:11 AM3/12/12
to open...@googlegroups.com
- 好吼!有固定链接了
- 俺就加到列表綴文中,长期提醒了 ;-)

Simon

unread,
Mar 12, 2012, 4:29:22 AM3/12/12
to openresty
cosocket很强大,如果文件存储在后端,lua模块只用来做porxy,那么完全可以自由拼接
后端的分块存储形成一个文件流给用户。

如果使用lua读写本地文件,那么缓存也能搞定,就是用不上sendfile,性能会差点。貌似
做个sendfile的lua bind是不是可以搞定?

Simon

unread,
Mar 12, 2012, 4:38:49 AM3/12/12
to openresty
又想了想,用lua读写文件,会阻塞线程。。。能主动yield么,或者改用luaaio?

agentzh

unread,
Mar 12, 2012, 5:11:12 AM3/12/12
to open...@googlegroups.com
On Mon, Mar 12, 2012 at 4:29 PM, Simon <big...@gmail.com> wrote:
cosocket很强大,如果文件存储在后端,lua模块只用来做porxy,那么完全可以自由拼接
后端的分块存储形成一个文件流给用户。


是的。
 
如果使用lua读写本地文件,那么缓存也能搞定,就是用不上sendfile,性能会差点。貌似
做个sendfile的lua bind是不是可以搞定?


sendfile 调用本身也是阻塞的,只是可以节约数据拷贝的时间。

Regards,
-agentzh

agentzh

unread,
Mar 12, 2012, 5:14:08 AM3/12/12
to open...@googlegroups.com
On Mon, Mar 12, 2012 at 4:38 PM, Simon <big...@gmail.com> wrote:
又想了想,用lua读写文件,会阻塞线程。。。能主动yield么,或者改用luaaio?


不能,因为 Nginx worker 进程是单线程的。luaaio 在这里应该帮助不大,因为需要和 nginx 事件模型整合在一起。同时,至少在 Linux 上,aio 也有许多限制和问题。

Nginx 2 有计划引入内部 OS 线程池来规避磁盘 I/O 对主线程的阻塞,但多线程的引入容易同时引入其他问题。。。

Regards,
-agentzh

Simon

unread,
Mar 12, 2012, 9:41:30 PM3/12/12
to openresty
恩,如果ngx_lua的磁盘io能和nginx的事件模型整合一起就完美了,哈哈

不过有这种大量io的应用场景不多,呵呵

On Mar 12, 5:14 pm, agentzh <agen...@gmail.com> wrote:

agentzh

unread,
Mar 15, 2012, 6:08:53 AM3/15/12
to open...@googlegroups.com
On Sun, Mar 11, 2012 at 7:04 PM, agentzh <age...@gmail.com> wrote:
如果你的数据源是磁盘文件,可以使用类似下面的代码来分块读取文件内容并流式输出给下游 HTTP 连接:

   local chunk_size = 4096  -- this is just an example
   local file_path = "/tmp/big.txt"  -- this is just an example

   local f, err = io.open(file_path, "r")
   if not f then
       ngx.say("failed to open file: ", err)
       return
   end

   while true do
       local chunk = f:read(chunk_size)
       if not chunk then
           break
       end

       ngx.print(chunk)
       ngx.flush(true)
   end

这个例子中的关键是利用了 ngx.flush 这个接口:

http://wiki.nginx.org/HttpLuaModule#ngx.flush

不过这里需要注意的一个问题是 ngx_lua 对于 HTTP 1.0 是自动对响应体进行全缓存的(为了支持 HTTP 1.0 Keepalive),而目前没有提供配置方式禁止此行为,所以当客户端发起的是 HTTP 1.0 请求,上面的代码并不能实现流式输出。我这两天将实现一个 lua_http10_buffering 配置选项,上面的例子中只要在 nginx.conf 中加上

   lua_http10_buffering off;

就不会再有 HTTP 1.0 全缓存输出的行为了。


已经在 ngx_lua 0.5.0rc19 和 ngx_openresty 1.0.11.23 中包含了这个新的配置指令。文档在这里:

http://wiki.nginx.org/HttpLuaModule#lua_http10_buffering

新版本中还修复了 ngx.flush() 函数在请求内部的泄漏问题。

Best regards,
-agentzh

乔巴乔巴

unread,
Dec 18, 2014, 1:14:38 AM12/18/14
to open...@googlegroups.com

如果文件存储在后端,lua模块做proxy,在流式输出给用户的时候,有没有可能利用lua coroutine去并发输出多个文件到输出流里面?


在 2012年3月12日星期一UTC+8下午4时29分22秒,Simon写道:

乔巴乔巴

unread,
Dec 18, 2014, 1:18:22 AM12/18/14
to open...@googlegroups.com

想让用户下载的是zip包,里面包含多个文件,lua能不能并发的输出多个文件到zip包里。


在 2014年12月18日星期四UTC+8下午2时14分38秒,乔巴乔巴写道:

Yichun Zhang (agentzh)

unread,
Dec 18, 2014, 3:30:10 PM12/18/14
to openresty
Hello!

2014-12-17 22:14 GMT-08:00 乔巴乔巴:
>
> 如果文件存储在后端,lua模块做proxy,在流式输出给用户的时候,有没有可能利用lua coroutine去并发输出多个文件到输出流里面?
>

单个请求同部的多个后端的并发访问可以使用轻量级线程 API:

https://github.com/openresty/lua-nginx-module#ngxthreadspawn

Regards,
-agentzh

xiati...@gmail.com

unread,
Dec 25, 2014, 3:44:42 AM12/25/14
to open...@googlegroups.com
         章大哥和各位大神,我想请问一下,我用以下的代码做了一个http下载服务器进行测试,捉包发现http的头部没有Content-Length这个字段,请问是为什么会这样呢?要怎么配置才能有Content-Length这个字段。。。

在 2012年3月11日星期日UTC+8下午7时04分20秒,agentzh写道:

Yichun Zhang (agentzh)

unread,
Dec 26, 2014, 3:58:11 PM12/26/14
to openresty
Hello!

2014-12-25 0:44 GMT-08:00 xiatian1071:
>
> 章大哥和各位大神,我想请问一下,我用以下的代码做了一个http下载服务器进行测试,捉包发现http的头部没有Content-Length这个字段,请问是为什么会这样呢?要怎么配置才能有Content-Length这个字段。。。
>

需要你在开头自己通过 ngx.header["Content-Length"] 设置 Content-Length 这个响应头。

你可以通过第三方的 Lua 库事先获取文件的大小,然后作为 Content-Length 的值。

值得一提的是,Content-Length 响应头并不是必需的,毕竟 HTTP 1.1 协议有 chunked encoding.

Regards,
-agentzh
Reply all
Reply to author
Forward
0 new messages