我正在尝试从Udacity的Full Stack Foundations课程中进行练习。我在do_POST
的子类中有BaseHTTPRequestHandler
方法,基本上我想得到一个名为message
的帖子值,用多部分表单提交,这是方法的代码:
def do_POST(self):
try:
if self.path.endswith("/Hello"):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers
ctype, pdict = cgi.parse_header(self.headers['content-type'])
if ctype == 'multipart/form-data':
fields = cgi.parse_multipart(self.rfile, pdict)
messagecontent = fields.get('message')
output = ""
output += "<html><body>"
output += "<h2>Ok, how about this?</h2>"
output += "<h1>{}</h1>".format(messagecontent)
output += "<form method='POST' enctype='multipart/form-data' action='/Hello'>"
output += "<h2>What would you like to say?</h2>"
output += "<input name='message' type='text'/><br/><input type='submit' value='Submit'/>"
output += "</form></body></html>"
self.wfile.write(output.encode('utf-8'))
print(output)
return
except:
self.send_error(404, "{}".format(sys.exc_info()[0]))
print(sys.exc_info() )
问题是cgi.parse_multipart(self.rfile, pdict)
抛出异常:TypeError: can't concat bytes to str
,实现是在课程的视频中提供的,但是他们使用的是Python 2.7并且我使用的是python 3,我整个下午都在寻找解决方案但是我找不到任何有用的东西,读取从python 3中的多部分表单传递的数据的正确方法是什么?
我在这里遇到了像你一样解决同样的问题。我找到了一个愚蠢的解决方案。我只是将字典中的'boundary'项从字符串转换为带有编码选项的字节。
ctype, pdict = cgi.parse_header(self.headers['content-type'])
pdict['boundary'] = bytes(pdict['boundary'], "utf-8")
if ctype == 'multipart/form-data':
fields = cgi.parse_multipart(self.rfile, pdict)
在我的情况下,似乎工作正常。
要将教师的代码更改为适用于Python 3,您需要打三个错误消息:
如果您收到这些错误消息
c_type, p_dict = cgi.parse_header(self.headers.getheader('Content-Type'))
AttributeError: 'HTTPMessage' object has no attribute 'getheader'
要么
boundary = pdict['boundary'].decode('ascii')
AttributeError: 'str' object has no attribute 'decode'
要么
headers['Content-Length'] = pdict['CONTENT-LENGTH']
KeyError: 'CONTENT-LENGTH'
跑步的时候
c_type, p_dict = cgi.parse_header(self.headers.getheader('Content-Type'))
if c_type == 'multipart/form-data':
fields = cgi.parse_multipart(self.rfile, p_dict)
message_content = fields.get('message')
这适用于你。
解
首先改变第一行以适应Python 3:
- c_type, p_dict = cgi.parse_header(self.headers.getheader('Content-Type'))
+ c_type, p_dict = cgi.parse_header(self.headers.get('Content-Type'))
其次,要修复'str'对象没有任何属性'decode'的错误,这是因为从Python 3开始将字符串的变化转换为unicode字符串,而不是像Python 3那样等效于字节字符串,所以添加这条线就在上面一条:
p_dict['boundary'] = bytes(p_dict['boundary'], "utf-8")
第三,要修复pdict中没有'CONTENT-LENGTH'的错误,只需在if语句之前添加以下行:
content_len = int(self.headers.get('Content-length'))
p_dict['CONTENT-LENGTH'] = content_len
我的Github上的完整解决方案:
我正在做同样的课程并遇到同样的问题。我现在使用的是解析库,而不是让它与cgi一起使用。这只是前几节课中的相同课程。
from urllib.parse import parse_qs
length = int(self.headers.get('Content-length', 0))
body = self.rfile.read(length).decode()
params = parse_qs(body)
messagecontent = params["message"][0]
你必须摆脱你的形式enctype='multipart/form-data'
。
另一个黑客解决方案是编辑cgi
模块的源代码。
在parse_multipart
的最开始(大约第226行):将boundary
的用法改为str(boundary)
...
boundary = b""
if 'boundary' in pdict:
boundary = pdict['boundary']
if not valid_boundary(boundary):
raise ValueError('Invalid boundary in multipart form: %r'
% (boundary,))
nextpart = b"--" + str(boundary)
lastpart = b"--" + str(boundary) + b"--"
...
在我的情况下,我使用cgi.FieldStorage
提取文件和名称而不是cgi.parse_multipart
form = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={'REQUEST_METHOD':'POST',
'CONTENT_TYPE':self.headers['Content-Type'],
})
print('File', form['file'].file.read())
print('Name', form['name'].value)