OpenResty 使用 lua-resty-upload 上传文件

OpenResty Lua 大约 3909 字

说明

lua-resty-upload内置于OpenResty中,可直接require即可。

Linux平台文件保存路径需为绝对路径,不能使用相对路径。

Windows平台上传的文件无法打开使用io.open函数传入的模式为w+bb:以二进制方式写入,避免不通平台换行符问题。Linuxw+即可,w+b也同样适用),可参考之前文章:https://www.zhangbj.com/p/641.html

备注

对于反向代理的接口需设置HostX-Scheme获取反向代理前的信息。

location /api/ {
    proxy_connect_timeout 5; # 单位:秒
    proxy_set_header Host $host; # 转发来源 host
    proxy_set_header X-Scheme $scheme; # 转发来源 scheme
    proxy_pass http://127.0.0.1:9527/;
}

示例代码

local json = require "cjson.safe"
local const = require "module.const"
local upload = require "resty.upload"

--local root_path = "z_blog_openresty/resources/img/" .. ngx.re.gsub(ngx.today(), "-", "") .. "/"
local relative_path = "uploads/img/"
-- ngx.var.realpath_root Windows输出:./html Linux输出:/usr/local/openresty/nginx/html
-- ngx.var.document_root Windows输出:./html Linux输出:/usr/local/openresty/nginx/html
--local root_path = "z_blog_openresty/resources/" .. relative_path 如果只用相对路径,Linux上将报错
local root_path = ngx.var.document_root .. "/../z_blog_openresty/resources/" .. relative_path

local chunk_size = 4096
local form = upload:new(chunk_size)

local file

local file_name

while true do
    local typ, res, err = form:read()

    if not typ then
        ngx.log(ngx.ERR, "upload img failed to read#" .. err)
        ngx.say(json.encode(const.fail("failed to read#" .. err)))
        return
    end

    if typ == "header" then
        -- res
        -- ["Content-Disposition","form-data; name=\"image\"; filename=\"aa.aa.png\"","Content-Disposition: form-data; name=\"image\"; filename=\"aa.png\""]

        -- 限制 image 字段
        local param_info = ngx.re.match(res[2], [[form-data; name="(.+)";]])
        -- {"0":"form-data; name=\"image\";","1":"image"}

        if param_info then
            local param = param_info[1]
            if param ~= "image" then
                ngx.log(ngx.ERR, "upload img invalid param#" .. param)
                ngx.say(json.encode(const.fail("invalid param#" .. param)))
                return
            end

            local file_info = ngx.re.match(res[2], [[filename="(.+)\.(.+)"]])
            --  {"0":"filename=\"aa.aa.png\"","1":"aa.aa","2":"png"}

            if file_info then
                local file_extension = file_info[2]
                local typ_match = ngx.re.match(file_extension, [[(png)|(jpeg)|(jpg)|(gif)|(svg)|(bmp)|(webp)]], "i")

                if not typ_match then
                    ngx.log(ngx.ERR, "upload img file type error#" .. file_extension)
                    ngx.say(json.encode(const.fail("file type error#" .. file_extension)))
                    return
                end

                file_name = ngx.md5(ngx.now() .. ngx.var.pid .. file_info[1]) .. "." .. file_extension:lower()
                file = io.open(root_path .. file_name, "w+b")
                if not file then
                    ngx.log(ngx.ERR, "upload img failed to open file#" .. root_path .. file_name)
                    ngx.say(json.encode(const.fail("failed to open file#" .. file_name)))
                    return
                end
            end
        end
    elseif typ == "body" then
        if file then
            file:write(res)
        end
    elseif typ == "part_end" then
        if file then
            file:close()
            file = nil
        end
    elseif typ == "eof" then
        break
    else
        -- do nothing
    end
end

if not file_name then
    ngx.log(ngx.ERR, "upload img invalid file#" .. ngx.var.http_user_agent)
    ngx.say(json.encode(const.fail("invalid file")))
    return
end

--微信小程序中图片不支持直接斜杠+名称的格式 /aaa.jpg
--local file_url = string.format("/%s%s", relative_path, file_name)

--local file_url = string.format("%s://%s/%s%s", ngx.var.scheme, ngx.var.host, relative_path, file_name)

local file_url = string.format("%s://%s/%s%s", ngx.req.get_headers()['X-Scheme'], ngx.var.host, relative_path, file_name)

ngx.say(json.encode(const.ok(file_url)))
阅读 205 · 发布于 2021-03-02

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批