cgi.parse_multipart函数在Python 3中抛出TypeError

问题描述 投票:8回答:5

我正在尝试从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中的多部分表单传递的数据的正确方法是什么?

forms python-3.x cgi
5个回答
14
投票

我在这里遇到了像你一样解决同样的问题。我找到了一个愚蠢的解决方案。我只是将字典中的'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)

在我的情况下,似乎工作正常。


3
投票

要将教师的代码更改为适用于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上的完整解决方案:

https://github.com/rSkogeby/web-server


1
投票

我正在做同样的课程并遇到同样的问题。我现在使用的是解析库,而不是让它与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'


0
投票

另一个黑客解决方案是编辑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"--" 
...

0
投票

在我的情况下,我使用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)
© www.soinside.com 2019 - 2024. All rights reserved.