清闲居 2019-06-21
大多数前端工程师对于这个multipart/form-data并不陌生,当我们需要发送二进制数据如图片时,通常会用到这个玩意儿~我们用form表单提交数据时,会指定form元素的enctype属性值为multipart/form-data,又或者使用html5新对象Formdata,我们用ajax发送数据时会指定content-type为multipart/form-data.
multipart/form-data数据格式
我们来看看这个multipart/form-data究竟有什么特别之处。 Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC Host: w.sohu.com --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC Content-Disposition: form-data;name="desc" Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [......][......][......][......]........................... --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC Content-Disposition: form-data;name="pic"; filename="photo.jpg" Content-Type: application/octet-stream Content-Transfer-Encoding: binary [图片二进制数据] --ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC-- 以上是截取了一个典型的multipart/form-data格式的http请求的部分,我们逐行来分析。 首先第一行,很简单,指定了Content-Type值,表明了这条http请求是multipart/form-data类型的。 细心的你可能发现了有一个名为boundary的玩意儿,它的值是一串看起来毫无规律的字符串。 先不急,我们来看一下具体的数据~分析数据我们可以看到刚才提到的boundary值在数据体中重复出现了多次。没错,这个boundary就是用来分割不同数据块的,当你提交这个请求后,后台会根据你在content-type中指定的boundary值来解析你的数据。 Content-Disposition用来表明该数据是表单数据,name用来说明这块数据块的名称,当是二进制数据时,你还需指定filename,即文件名。 multipart/form-data对数据格式要求非常严格,换行时必须使用\r\n,而不是\n,分隔符boundary在使用时,必须加上"--",即--boundary\r\n,数据体完结后,用--boundary--表明结束
稍微介绍了multipart/form-data的相关知识,我们现在进入重点:使用Lua来构造multipart/form-data格式的数据,并与webserver交互(在这里使用php)
我们需要使用到lua的一个模块,socket.http(安装引用不在本文讨论范围,请读者自行学习)
local resbody = {} local reqfile= io.open(your-file-path) local file_attr = lfs.attributes(your-file-path) local size = file_attr.size --获取文件大小 local body, code, headers, status = http.request { method = "POST", url ='http://xxxx/upload.php', headers = { ["Content-Type"] = "multipart/form-data", ["Content-Length"] = size }, source = ltn12.source.file(reqfile), sink = ltn12.sink.table(respbody) } 注意,以上代码是我从stackoverflow上看到类似的,提问者自称能成功发送,但是这样的方式,在php里只能用file_get_contents( php://input )来获取原始数据流,但是$_POST和$_FILES数组拿不到你的数据,这显然不是我们想要的。 构造数据: local respbody = {} local _file = [[--abcd]]..'\r\n'..[[Content-Disposition: form-data; name="myfile"; filename="1.jpg"]]..'\r\n'..[[Content-Type: image/jpeg]]..'\r\n\r\n' local _table1 = '\r\n'..[[--abcd]]..'\r\n'..[[Content-Disposition: form-data; name="type";]]..'\r\n\r\n'..[[0]] local _table2 = '\r\n'..[[--abcd]]..'\r\n'..[[Content-Disposition: form-data; name="themeName";]]..'\r\n\r\n'..[[1482753000731]] local _end ='\r\n'..[[--abcd--]]..'\r\n' local reqfile= io.open(your-file-path) local file_attr = lfs.attributes(your-file-path) local size = file_attr.size local body, code, headers, status = http.request { method = "POST", url = 'http://xxxx/upload.php', headers = { ["Content-Type"] = "multipart/form-data;boundary=abcd", ["Content-Length"] = size+#_file+#_table1+#_table2+#_end }, source = ltn12.source.cat(ltn12.source.string(_file),ltn12.source.file(reqfile),ltn12.source.string(_table1),ltn12.source.string(_table2),ltn12.source.string(_end)), sink = ltn12.sink.table(respbody) } 注意:我们这里进行了字符串块的拼接,ltn12.source.string()只能接受字符串块,这里的拼接过程中,换行符\r\n需要特别注意。
以上就是使用Lua构造multipar/form-data格式数据,并发送请求的全部内容,感谢阅读。