高级CGI
mulitipart 表单提交和文件上传
. CGI 特别指出只允许两种表单编码:“application/x-www-form-urlencoded”和“multipart/form-dat”。且前者是默认的,因此前者不需要特别指出,但是后者需要明确给出编码:
<FORM enctype="multipart/form-data" ...>
. 表单提交的时候可以使用任意编码,但是在上传文件的时候只能使用multipart编码,通过使用输入类型为文件的方式来完成:
<INPUT type=file name=...>
. 这个指令显示一个空的文本框,同时旁边有个按钮,可以通过该按钮浏览文件目录结构
多值字段
. 常见的处理多值字段的情况是有一系列的复选框,在提交表单的时候,数据以键值对的方式传递给服务器,当提交的不止一个复选框的时候,就会出现多个值对应一个键。在这种情况下,cgi模块会建立一个包含这类实例的列表,可以遍历该列表获得所有值。
cookie
. 可以将其看做web站点服务器要求保存在客户端上的二进制数据。
从一个页面跳转到另一个页面的方式可以是通过GET请求中的键值对来实现;也可以通过使用隐藏的表单字段,就像是一个标识符,找得到这个标识符就显示这个页面。这些变量和值由服务器托管,因为这些信息必须嵌入到新生成的页面中并返回给客户端。
另一个可以保持多个页面连续浏览的方式是在客户端保存这些数据,这就是引入cookie的原因。服务器向客户端发送一个请求来保存 cookie,而不必用在返回的 Web 页面中嵌入数据的方法来保持数据。cookie 连接到最初服务器的主域上(这样一个服务器就不能设置或者覆盖其他 Web 站点中的 cookie),并且有一定的存储期限(因此浏览器不会堆满 cookie)。
cookie 和文件上传
. 接下来的例子展示了cookie和文件上传,当第一次登录网页,没有缓存,表单中的输入框都是空的,生成过结果页面后,脚本设置了cookie,缓存在浏览器中,当再次回到表单页面的时候,会自动填入相应的值:
import cgi
from urllib.parse import quote,unquote
import os
import io
class AdvCGI(object):
header = 'Content-Type: text/html\n\n'
url = "/cgi-bin/advcgi.py"
formhtml = '''<html><head><tltle>
Advanced CGI demon</title></head>
<body><h2>Advanced CGI Demo Form:</h2>
<form method = post action = "%s" enctype = "multipart/form-data">
<h3>my cookie setting</h3>
<li><code><b>CPPuser = %s</b></code>
<h3>enter cookie value:<br>
<input name = cookie value = %s> (<i>optional</i>)</h3>
<h3>enter your name:<br>
<input name = person value = %s> (<i>required</i>)</h3>
<h3>what languages can you program in? (<i>at least one required</i>)</h3>
%s
<h3>enter file to upload <small>(max size 4K)</samll></h3>
<input type = file name = upfile value = "%s" size = 45>
<p><input type = submit>
</form></body></html>
'''
langset = ('Python','C','C++','Ruby','Java','PHP')
langitem = '<input type = checkbox name = lang value = "%s" %s>%s\n'
# 从客户端(浏览器)读取cookie,这个方法只会被showform方法调用
def getcookie(self):
if 'HTTP_COOKIE' in os.environ:
#使用HTTP_COOKIE这个环境变量访问cookie
cookies = [x.strip() for x in os.environ['HTTP_COOKIE'].split(';')]
for eachCookie in cookies:
if len(eachCookie)>6 and eachCookie[:3]=='CPP':
tag = eachCookie[3:7]
try:
self.cookies[tag] = eval(unquote(eachCookie[8:]))
except:
self.cookies[tag] = unquote(eachCookie[8:])
if 'info' not in self.cookies:
self.cookies['info'] = ''
if 'user' not in self.cookies:
self.cookies['user'] = ''
else:
self.cookies['info'] = self.cookies['user'] = ''
if self.cookies['info'] != '':
self.who,langStr,self.fn = self.cookies['info'].split(':')
self.langs = langStr.split(',')
else:
self.who = self.fn = ''
self.langs = ['Python']
#用于显示初始界面
def showForm(self):
self.getcookie()
#放置复选框
langStr = []
for eachlang in AdvCGI.langset:
langStr.append(AdvCGI.langitem % (eachlang,'CHECKED' if eachlang in self.langs else '',eachlang))
if not ('user' in self.cookies and self.cookies['user']):
cookStatus = '<i>(cookie has not been set yet)</i>'
userCook = ''
else:
userCook = cookStatus = self.cookies['user']
print('%s%s' % (AdvCGI.header,AdvCGI.formhtml % (AdvCGI.url,cookStatus,
userCook,self.who,
''.join(langStr),self.fn)))
#用于错误反馈
errhtml = '''html><head><tltle>
Advanced CGI demon</title></head>
<body><h3>Error</h3>
<b>%s</b><p>
<form><input type = button value = Back
ONCLICK = "window.history.back()"></form>
</body></html>
'''
def showErr(self):
print(AdvCGI.header + AdvCGI.errhtml % self.err)
#用于生成结果信息页
reshtml = '''<html><head><tltle>
Advanced CGI demon</title></head>
<h3>your cookies value is: <b>%s</b></h3>
<h3>your name is: <b>%s</b></h3>
<h3>your can program in the fallowing languages:</h3>
<ul>%s</ul>
<h3>your uoloaded file...<br>
name:<i>%s</i><br>
contents:</h3>
<pre>%s</pre>
Click <a href = "%s"><b>here</b></a>to return to form
</body></html>
'''
#这里设置了cookies,还加上了CPP前缀,和get那里是对应的
def setcookie(self):
for eachcookie in self.cookies.keys():
print('Set-Cookie: CPP%s=%s;path=/' % \
(eachcookie,quote(self.cookies[eachcookie])))
def doResult(self):
MAXBYTES = 4096
langlist = ''.join('<li>%s<br>' % eachlang for eachlang in self.langs)
filedata = self.fp.read(MAXBYTES)
if len(filedata) == MAXBYTES and f.read():
filedata = '%s%s' % (filedata,'... <b><i>(file truncated due to size)</i></b>')
self.fp.close()
if filedata == '':
filedata = '<b><i>(file not given or upload error)</i></b>'
filename = self.fn
if not ('user' in self.cookies and self.cookies['user']):
cookStatus = '<i>(cookie has not been set yet)</i>'
userCook = ''
else:
userCook = cookStatus = self.cookies['user']
self.cookies['info'] = ':'.join((self.who,','.join(self.langs),filename))
self.setcookie()
print('%s%s' % (AdvCGI.header,AdvCGI.reshtml % (cookStatus,self.who,langlist,
filename,filedata,AdvCGI.url)))
#决定跳转到哪一个页面
def go(self):
self.cookies = {}
self.err = ''
form = cgi.FieldStorage()
if not form.keys():
self.showForm()
return
if 'person' in form:
self.who = form['person'].value.strip().title()
if self.who == '':
self.err = 'your name is required.(blank)'
else:
self.err = 'your name is required.(miss)'
self.cookies['user'] = unquote(form['cookie'].value.strip()) if 'cookie' in form else ''
if 'lang' in form:
langData = form['lang']
#isinstance() 函数来判断一个对象是否是一个已知的类型
if isinstance(langData,list):
self.langs = [eachLang.value for eachLang in langData]
else:
self.langs = [langData.value]
else:
self.err = 'at least one language required'
if 'upfile' in form:
upfile = form['upfile']
#or 表示从左到右扫描,返回第一个为真的表达式值,无真值则返回最后一个表达式值。
self.fn = upfile.filename or ''
if upfile.file:
self.fp = upfile.file
else:
self.fp = io.StringIO('(no data)')
else:
self.fp = io.StringIO('(no file)')
self.fn = ''
if not self.err:
self.doResult()
else:
self.showErr()
if __name__ == '__main__':
page = AdvCGI()
page.go()