lua-resty-upload

基于 ngx_lua cosocket 的 HTTP 文件上传流读取器和解析器

$ opm get openresty/lua-resty-upload

名称

lua-resty-upload - 基于 ngx_lua cosocket 的 HTTP 文件上传流读取器和解析器

状态

该库被认为是生产就绪的。

描述

该 Lua 库是 ngx_lua nginx 模块的流文件上传 API

http://wiki.nginx.org/HttpLuaModule

支持 multipart/form-data MIME 类型。

该库的 API 只返回一个一个的令牌。用户只需要重复调用 read 方法,直到返回 nil 令牌类型。对于从 read 方法返回的每个令牌,只需检查第一个返回值以获取当前令牌类型。令牌类型可以是 headerbodypart end。每个解析的 multipart/form-data 表单字段包含多个 header 令牌,每个令牌都包含一个字段头,多个 body 令牌,每个令牌都包含一个主体数据块,以及一个 part end 标志,指示字段结束。

这就是流式读取的工作原理。即使对于千兆字节的文件数据输入,lua 领域的内存使用量也可以保持较小且恒定,只要用户自己不累积输入数据块。

该 Lua 库利用了 ngx_lua 的 cosocket API,从而确保了 100% 的非阻塞行为。

请注意,至少需要 ngx_lua 0.7.9OpenResty 1.2.4.14

概要

        lua_package_path "/path/to/lua-resty-upload/lib/?.lua;;";
    
        server {
            location /test {
                content_by_lua '
                    local upload = require "resty.upload"
                    local cjson = require "cjson"
    
                    local chunk_size = 5 -- should be set to 4096 or 8192
                                         -- for real-world settings
    
                    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
    
                    local typ, res, err = form:read()
                    ngx.say("read: ", cjson.encode({typ, res}))
                ';
            }
        }

上面定义的 /test 位置的典型输出是

    read: ["header",["Content-Disposition","form-data; name=\"file1\"; filename=\"a.txt\"","Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""]]
    read: ["header",["Content-Type","text\/plain","Content-Type: text\/plain"]]
    read: ["body","Hello"]
    read: ["body",", wor"]
    read: ["body","ld"]
    read: ["part_end"]
    read: ["header",["Content-Disposition","form-data; name=\"test\"","Content-Disposition: form-data; name=\"test\""]]
    read: ["body","value"]
    read: ["body","\r\n"]
    read: ["part_end"]
    read: ["eof"]
    read: ["eof"]

您可以使用 lua-resty-string 库来增量计算文件数据的 SHA-1 和 MD5 摘要。以下是一个这样的示例

        local resty_sha1 = require "resty.sha1"
        local upload = require "resty.upload"
    
        local chunk_size = 4096
        local form = upload:new(chunk_size)
        local sha1 = resty_sha1:new()
        local file
        while true do
            local typ, res, err = form:read()
    
            if not typ then
                 ngx.say("failed to read: ", err)
                 return
            end
    
            if typ == "header" then
                local file_name = my_get_file_name(res)
                if file_name then
                    file = io.open(file_name, "w+")
                    if not file then
                        ngx.say("failed to open file ", file_name)
                        return
                    end
                end
    
             elseif typ == "body" then
                if file then
                    file:write(res)
                    sha1:update(res)
                end
    
            elseif typ == "part_end" then
                file:close()
                file = nil
                local sha1_sum = sha1:final()
                sha1:reset()
                my_save_sha1_sum(sha1_sum)
    
            elseif typ == "eof" then
                break
    
            else
                -- do nothing
            end
        end

如果您想为上传的文件计算 MD5 校验和,只需使用 lua-resty-string 库提供的 resty.md5 模块即可。它具有与 resty.sha1 相似的 API。

对于大型文件上传,重要的是不要将所有数据缓冲在内存中。也就是说,您永远不应该在巨大的 Lua 字符串或巨大的 Lua 表中累积数据块。您必须尽快将数据块写入文件,并立即丢弃数据块(以便让 Lua GC 释放它)。

除了将数据块写入文件(如上面的示例所示)之外,您还可以将数据块写入上游 cosocket 连接,如果您不想在本地文件系统上保存数据。

作者

Yichun "agentzh" Zhang (章亦春) <[email protected]>,OpenResty Inc.

版权和许可

该模块是在 BSD 许可下授权的。

版权所有 (C) 2012-2017,由 Yichun "agentzh" Zhang (章亦春) <[email protected]>,OpenResty Inc.

保留所有权利。

在满足以下条件的情况下,允许以源代码和二进制形式重新分发和使用本软件,无论是否修改:

  • 源代码的重新分发必须保留上述版权声明、本条件列表和以下免责声明。

  • 二进制形式的重新分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、本条件列表和以下免责声明。

本软件由版权持有人和贡献者“按现状”提供,并且任何明示或暗示的担保,包括但不限于适销性和特定用途适用性的暗示担保,均被免除。在任何情况下,版权持有人或贡献者均不对任何直接、间接、偶然、特殊、惩罚性或后果性损害(包括但不限于替代商品或服务的采购;使用、数据或利润损失;或业务中断)负责,无论其原因是什么,以及任何责任理论,无论是合同、严格责任还是侵权(包括疏忽或其他原因),均因使用本软件而产生,即使已被告知可能发生此类损害。

另请参阅

作者

Yichun "agentzh" Zhang (agentzh)

许可

2bsd

版本