-- upload.lua --========================================== -- 文件上传 --========================================== local upload = require"resty.upload" local cjson = require"cjson" local chunk_size = 4096 local form, err = upload:new(chunk_size) ifnot form then ngx.log(ngx.ERR, "failed to new upload: ", err) ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end form:set_timeout(1000) -- 字符串 split 分割 string.split = function(s, p) local rt= {} string.gsub(s, '[^'..p..']+', function(w)table.insert(rt, w) end ) return rt end -- 支持字符串前后 trim string.trim = function(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end -- 文件保存的根路径 local saveRootPath = ngx.var.store_dir -- 保存的文件对象 local fileToSave --文件是否成功保存 local ret_save = false whiletruedo local typ, res, err = form:read() ifnot typ then ngx.say("failed to read: ", err) return end if typ == "header"then -- 开始读取 http header -- 解析出本次上传的文件名 local key = res[1] local value = res[2] if key == "Content-Disposition"then -- 解析出本次上传的文件名 -- form-data; name="testFileName"; filename="testfile.txt" local kvlist = string.split(value, ';') for _, kv inipairs(kvlist) do local seg = string.trim(kv) if seg:find("filename") then local kvfile = string.split(seg, "=") local filename = string.sub(kvfile[2], 2, -2) if filename then fileToSave = io.open(saveRootPath .. filename, "w+") ifnot fileToSave then ngx.say("saveRootPath: ", saveRootPath) ngx.say("failed to open file ", filename) return end break end end end end elseif typ == "body"then -- 开始读取 http body if fileToSave then fileToSave:write(res) end elseif typ == "part_end"then -- 文件写结束,关闭文件 if fileToSave then fileToSave:close() fileToSave = nil end
ret_save = true elseif typ == "eof"then -- 文件读取结束 break else ngx.log(ngx.INFO, "do other things") end end if ret_save then ngx.say("save file ok") end
接收逻辑的核心是循环调用 form:read():读到 header 就解析出文件名并创建目标文件,读到 body 就写入,part_end 关闭文件,eof 结束。