From 70ae2fa0c9a0173449539ba911ad7a6baf40c171 Mon Sep 17 00:00:00 2001 From: yinxiyi Date: Mon, 7 Jul 2014 13:59:54 +0800 Subject: [PATCH 1/3] support decode the multipart form data as lua table --- lib/resty/form.lua | 116 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 lib/resty/form.lua diff --git a/lib/resty/form.lua b/lib/resty/form.lua new file mode 100644 index 0000000..3808765 --- /dev/null +++ b/lib/resty/form.lua @@ -0,0 +1,116 @@ +--[[ +Description: + This script is aimed to extend the nginx lua module, to provide support of + decoding HTTP multipart form data into Lua table. + The script requires the upload module, which is from : + https://github.com/openresty/lua-resty-upload + +FileId: +$Id: form.lua +2014-07-04 09:12 星期五 + +License: +Copyright 2014, mochui.net, all rights reserved. +--]] + +module("form", package.seeall) + +local upload = require "upload"; + +local function decodeContentDisposition(value) + local ret; + + local typ, paras = string.match(value, "([%w%-%._]+);(.+)"); + + if typ then + ret = {}; + ret.dispositionType = typ; + ret.paras = {}; + + if paras then + for paraKey, paraValue in string.gmatch(paras, '([%w%.%-_]+)="([%w%.%-_]+)"') do + ret.paras[paraKey] = paraValue; + end + end + end + + return ret; +end + +local function decodeHeaderItem(key, value) + local ret; + + if key == "Content-Disposition" then + ret = decodeContentDisposition(value); + end + + if key == "Content-Type" then + ret = value; + end + + return ret; +end + +local function decodeHeader(res) + if type(res) == "table" and res[1] and res[2] then + key = res[1]; + value = decodeHeaderItem(res[1], res[2]); + else + key = res; + value = res; + end + + return key, value; +end + +function getFormTable() + local chunkSize = 4096; + local formdata = {}; + local part = {}; + part.headers = {}; + part.body = ""; + + local headers = {}; + + local form, err = upload:new(chunkSize); + if not form then + return nil, err; + end + + form:set_timeout(1000); + + while true do + local typ, res, err = form:read(); + + if not typ then + return nil, err; + end + + if typ == "header" then + local key, value = decodeHeader(res); + if key then + part.headers[key] = value; + else + return nil, "failed to decode header:" .. res; + end + end + + if typ == "body" then + part.body = part.body .. res; + end + + if typ == "part_end" then + table.insert(formdata, part); + part = {}; + part.headers = {}; + part.body = ""; + end + + if typ == "eof" then + break; + end + end + + return formdata, err; +end + From 7441369adce48dd2d61e3f6cb9ef180780ef0713 Mon Sep 17 00:00:00 2001 From: yinxiyi Date: Wed, 9 Jul 2014 16:09:38 +0800 Subject: [PATCH 2/3] write uploaded content as file if filename provided --- lib/resty/form.lua | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/resty/form.lua b/lib/resty/form.lua index 3808765..66d6206 100644 --- a/lib/resty/form.lua +++ b/lib/resty/form.lua @@ -7,7 +7,7 @@ Description: FileId: $Id: form.lua -2014-07-04 09:12 星期五 +2014-07-09 16:08 星期三 License: Copyright 2014, mochui.net, all rights reserved. @@ -63,7 +63,7 @@ local function decodeHeader(res) return key, value; end -function getFormTable() +function getFormTable(tmpFileFolder) local chunkSize = 4096; local formdata = {}; local part = {}; @@ -72,6 +72,7 @@ function getFormTable() local headers = {}; + util.log("start to decode form data"); local form, err = upload:new(chunkSize); if not form then return nil, err; @@ -88,6 +89,7 @@ function getFormTable() if typ == "header" then local key, value = decodeHeader(res); + util.log("header:" .. tostring(res[3])); if key then part.headers[key] = value; else @@ -96,14 +98,35 @@ function getFormTable() end if typ == "body" then - part.body = part.body .. res; + if part.headers["Content-Disposition"] + and part.headers["Content-Disposition"].paras + and part.headers["Content-Disposition"].paras.filename then + local filePath = tmpFileFolder or "/tmp"; + local fullFileName = filePath .. "/" + .. part.headers["Content-Disposition"].paras.filename; + if not part.tmpFd then + part.tmpFd, err = io.open(fullFileName, "w"); + if not part.tmpFd then + return nil, "failed to open tmpfile:" + .. tostring(fullFileName); + end + part.body = fullFileName; + end + part.tmpFd:write(res); + else + part.body = part.body .. res; + end end if typ == "part_end" then + if part.tmpFd then + part.tmpFd:close(); + end table.insert(formdata, part); part = {}; part.headers = {}; - part.body = ""; + part.body = ""; + part.tmpFd = nil; end if typ == "eof" then From 1c4e9acb6dd080077368ce302fbdfea836899afa Mon Sep 17 00:00:00 2001 From: fundon Date: Thu, 31 Jul 2014 16:44:29 +0800 Subject: [PATCH 3/3] improve filename parse, fix filename is fullpath on IE8 --- lib/resty/form.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/resty/form.lua b/lib/resty/form.lua index 66d6206..58eea7e 100644 --- a/lib/resty/form.lua +++ b/lib/resty/form.lua @@ -26,9 +26,15 @@ local function decodeContentDisposition(value) ret = {}; ret.dispositionType = typ; ret.paras = {}; - + if paras then - for paraKey, paraValue in string.gmatch(paras, '([%w%.%-_]+)="([%w%.%-_]+)"') do + for paraKey, paraValue in string.gmatch(paras, '([%w%.%-_]+)="([^=;]+)"') do + -- Fixed, IE <= 8 problem, filename is fullpath, "c:\Users\fuckIE\xx.tar.gz", other borwsers just have a basename "xx.tar.gz". + if paraKey == "filename" then + local basename, _ = string.match(paraValue, '[^\\]+$') or "" + paraValue = basename; + end + ret.paras[paraKey] = paraValue; end end @@ -69,7 +75,7 @@ function getFormTable(tmpFileFolder) local part = {}; part.headers = {}; part.body = ""; - + local headers = {}; util.log("start to decode form data"); @@ -96,7 +102,7 @@ function getFormTable(tmpFileFolder) return nil, "failed to decode header:" .. res; end end - + if typ == "body" then if part.headers["Content-Disposition"] and part.headers["Content-Disposition"].paras