web框架之cgi.FieldStorage()与数据提交
来源:互联网 发布:淘宝会员等级怎么看 编辑:程序博客网 时间:2024/05/29 05:55
POST 一般用来向服务端提交数据,HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。
<method> <request-URL> <version><headers><entity-body>协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。
multipart/form-data:这是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 form 的 enctype 等于这个值。
请求示例:
POST http://www.example.com HTTP/1.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="text"title------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="file"; filename="chrome.png"Content-Type: image/pngPNG ... content of chrome.png ...------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
这种方式一般用来上传文件(Content-Disposition后可接form-data表示表单数据,attachment表示附件)。RFC2183
消息主体<entity-body>里按照字段个数又分为多个结构类似的部分:
每部分都是以 --boundary 开始,紧接着内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如 text 的值就是 title
如果传输的是文件,还要包含文件名和文件类型(Content-Type)信息。如文件名为chrome.png的文件类型是image/png,具体内容(值)就是‘ PNG ... content of chrome.png ... ’ (二进制数据)
消息主体最后以 --boundary-- 标示结束。
参考资料:https://www.imququ.com/post/four-ways-to-post-data-in-http.html(挺好的资料)
下面分析web框架中分析post请求实体<entity-post>的代码:
# coding=utf-8import cgidef _to_unicode(s, encoding='utf-8'): return s.decode('utf-8')class MultipartFile(object): def __init__(self, storage): self.filename = _to_unicode(storage.filename) self.file = storage.fileclass Request(object): def __init__(self, environ): self._environ = environ def _parse_input(self): def _convert(item): if isinstance(item, list): return [_to_unicode(i.value) for i in item] if item.filename: return MultipartFile(item) return _to_unicode(item.value) fs = cgi.FieldStorage(fp=self._environ['wsgi.input'], environ=self._environ, keep_blank_values=True) inputs = {} for key in fs: inputs[key] = _convert(fs[key]) #key的value可以是一个list return inputs def _get_raw_input(self): if not hasattr(self, '_raw_input'): self._raw_input = self._parse_input() return self._raw_input def __getitem__(self, key): r = self._get_raw_input()[key] if isinstance(r, list): return r[0] return r def get(self, key, default=None): r = self._get_raw_input().get(key, default) if isinstance(r, list): return r[0] return r def gets(self, key): r = self._get_raw_input()[key] if isinstance(r, list): return r[:] return [r]from StringIO import StringIOr = Request({'REQUEST_METHOD':'POST', 'wsgi.input':StringIO('a=1&b=M%20M&c=ABC&c=XYZ&e=')})print r['a']print r['c']b = '----WebKitFormBoundaryQQ3J8kPsjFpTmqNz' #这是分割线,用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂pl = ['--%s' % b, 'Content-Disposition: form-data; name=\"name\"\n', 'Scofield', '--%s' % b, 'Content-Disposition: form-data; name=\\"name\\"\\n', 'Lincoln', '--%s' % b, 'Content-Disposition: form-data; name=\\"file\\"; filename=\\"test.txt\\"', 'Content-Type: text/plain\\n', 'just a test', '--%s' % b, 'Content-Disposition: form-data; name=\\"id\\"\\n', '4008009001', '--%s--' % b, '']payload = '\n'.join(pl)r = Request({'REQUEST_METHOD':'POST', 'CONTENT_LENGTH':str(len(payload)), 'CONTENT_TYPE':'multipart/form-data; boundary=%s' % b, 'wsgi.input':StringIO(payload)})r.get('name')r.gets('name')f = r.get('file')print f.filename#print f.file.read()
payload(pl)就是post请求实体<entity-post>部分(这里是手动给的用来测试,正常情况下都是由浏览器客户端发来的),类似于我们上面的“ 请求示例”,存储在wsgi server的environ. wsgi.input中( 'wsgi.input':StringIO(payload) ),利用cgi.FieldStorage( )可以分解出其中的变量。
相应的,如果是浏览器客户端发来上面的请求,应该是这样的:
###################################|||<method> <request-URL> <version>###################################POST / HTTP/1.1'''#############|||<headers>#############boundary是自定义的一串复杂无规律的字符串Content-Type:有附件时类型务必是multipart/form-data;无附件(仅post表单)默认类型是application/x-www-form-urlencoded.'''Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryQQ3J8kPsjFpTmqNz################|||<entity-body>################------WebKitFormBoundaryQQ3J8kPsjFpTmqNzContent-Disposition: form-data; name="name"Scofield------WebKitFormBoundaryQQ3J8kPsjFpTmqNzContent-Disposition: form-data; name="name"Lincoln------WebKitFormBoundaryQQ3J8kPsjFpTmqNzContent-Disposition: form-data; name="file"; filename="test.txt"Content-Type: text/plainjust a test------WebKitFormBoundaryQQ3J8kPsjFpTmqNzContent-Disposition: form-data; name="id"4008009001------WebKitFormBoundaryQQ3J8kPsjFpTmqNz--前端对应的HTML表单是这样的(也就是cgi.FieldStorage()调用的东西):
浏览器显示出来是这样的:
注:test.txt文本文档里面的内容是 " just a test " .
可从Web框架中得到启发:
form = cgi.FieldStorage() # 获取文件名 fileitem = form['file'] # 检测文件是否上传 if fileitem.filename: # 设置文件路径 fn = os.path.basename(fileitem.filename) open('/tmp/' + fn, 'wb').write(fileitem.file.read())
其中 ' file '是HTML表单name属性的名称:
结果:
如图所示,file文件类型的name属性名称是“ file ”,text文本框的name属性名称是“ trial ”,cgi.FieldStorage()就是通过“ file ”和“ trial ”来获取相关表单信息的。form['file']或form[ ' trial ' ]表示一个input标签整体,而标签有属性,如可以通过form['file'].filename或form['file'].file等来获取文件的相关信息,通过form[ ' trial ' ].value来获取text文本框里面的值。
回到web框架的源代码就容易理解了。
fs [key] 的fs是cgi.FieldStorage()实例,代表整个表单,key就是表单中各个标签的“name”属性的名称——"name","name","file","id",而 fs [key] 则表示一个input标签整体!可用标签属性来获取标签的具体内容。
name---- " name "-----Scofield(fs [name].value,标签的value属性可获取text的内容)
name---- " name "-----Lincoln (fs [name].value,标签的value属性可获取text的内容)
name----- " file "--------" test.txt "(fs [file].filename,标签的filename属性可获取文件的名称)
name----- " id "----------" 4008009001 "(fs [id].value,标签的value属性可获取text的内容)
出现了两个fs [name],可遍历后 return一个list(' return [_to_unicode(i.value) for i in item] ');对于文件类型的,通过判断其 filename 属性进行相应处理!
- web框架之cgi.FieldStorage()与数据提交
- cgi.FieldStorage()获取网页间提交的数据《Head First Python》第七章
- php-cgi 数据重复提交
- CGI与Web开发
- CGI原理解析之二------WEB服务和CGI交互数据
- CGI原理解析系列之三----CGI如何完整获取WEB服务器数据
- HttpClient框架提交数据
- python核心编程学习笔记-2016-09-16-01-cgi.FieldStorage()是经典类
- CGI编程中提交表单的数据问题
- 实现提交数据到cgi,网页不跳转的方法
- CGI与WEB服务器的响应头
- 嵌入式web之CGI编程基本知识
- shiro 框架之与WEB集成
- CGI提交表单的两种方式POST与GET
- 嵌入式CGI 与HTML的数据交换
- 使用AsyncHttpClient 框架提交数据
- java web 提交数据乱码
- web表单提交数组数据
- Ubuntu 更新错误修复大全
- 禁止输入框显示用户历史输入历史记录
- 利用boostrap模拟框做上传
- 钥匙计数之一(递推)hdu1438
- eclipse 下安装hadoop2.5.2插件
- web框架之cgi.FieldStorage()与数据提交
- 创业企业的股权分配
- android学习笔记一 ---- android-sdk基础介绍
- 黑马程序员——Java基础——String类、StringBuffer和StringBuilder
- PAT 1035 插入与归并始终有一个点过不去
- hadoop2.5.2+zookeeper3.4.6+hbase0.99.2
- Android学习第一课:自定义日志LogCat输出
- 如何修改Ubuntu的源列表(source list)
- Android中Adapter之SimpleCursorAdapter使用