我正确解析这个HTTP POST请求了吗?
首先,我想说的是,我正在使用 twisted.web
框架。Twisted.web
的文件上传功能没有按照我想要的方式工作(它只包含文件数据,而没有其他信息),cgi.parse_multipart
也没有达到我的期望(同样的情况,twisted.web
使用了这个函数),cgi.FieldStorage
也不行(因为我通过 twisted 获取 POST 数据,而不是通过 CGI 接口——据我所知,FieldStorage
是通过标准输入来获取请求的),而且 twisted.web2
也不适合我,因为使用 Deferred
让我感到困惑和恼火(对我来说太复杂了)。
话虽如此,我决定尝试自己解析 HTTP 请求。
在 Chrome 中,HTTP 请求是这样形成的:
------WebKitFormBoundary7fouZ8mEjlCe92pq
Content-Disposition: form-data; name="upload_file_nonce"
11b03b61-9252-11df-a357-00266c608adb
------WebKitFormBoundary7fouZ8mEjlCe92pq
Content-Disposition: form-data; name="file"; filename="login.html"
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
...
------WebKitFormBoundary7fouZ8mEjlCe92pq
Content-Disposition: form-data; name="file"; filename=""
------WebKitFormBoundary7fouZ8mEjlCe92pq--
这总是这样形成的吗?我用正则表达式来解析它,像这样(请原谅我代码的冗长):
(注意,我剪掉了大部分代码,只显示我认为相关的部分(正则表达式(是的,嵌套的括号),这是我构建的 Uploads
类中的一个 __init__
方法(目前为止唯一的方法)。完整代码可以在修订历史中查看(希望我没有搞错任何括号))
if line == "--{0}--".format(boundary):
finished = True
if in_header == True and not line:
in_header = False
if 'type' not in current_file:
ignore_current_file = True
if in_header == True:
m = re.match(
"Content-Disposition: form-data; name=\"(.*?)\"; filename=\"(.*?)\"$", line)
if m:
input_name, current_file['filename'] = m.group(1), m.group(2)
m = re.match("Content-Type: (.*)$", line)
if m:
current_file['type'] = m.group(1)
else:
if 'data' not in current_file:
current_file['data'] = line
else:
current_file['data'] += line
你可以看到,每当达到一个边界时,我就会开始一个新的“文件”字典。我把 in_header
设置为 True
,表示我正在解析头部。当我遇到一个空行时,我把它改为 False
——但在此之前我会检查是否为该表单值设置了 Content-Type
——如果没有,我就把 ignore_current_file
设置为真,因为我只关注文件上传。
我知道我应该使用一个库,但我已经厌倦了阅读文档,尝试让不同的解决方案在我的项目中工作,同时还要让代码看起来合理。我只想尽快通过这一部分——如果解析带文件上传的 HTTP POST 这么简单,那我就继续这样做。
注意:这段代码现在运行得很好,我只是想知道它是否会在某些浏览器的请求上出错。
3 个回答
我解决这个问题的方法是使用 cgi.FieldStorage 来解析内容,像这样:
class Root(Resource):
def render_POST(self, request):
self.headers = request.getAllHeaders()
# For the parsing part look at [PyMOTW by Doug Hellmann][1]
img = cgi.FieldStorage(
fp = request.content,
headers = self.headers,
environ = {'REQUEST_METHOD':'POST',
'CONTENT_TYPE': self.headers['content-type'],
}
)
print img["upl_file"].name, img["upl_file"].filename,
print img["upl_file"].type, img["upl_file"].type
out = open(img["upl_file"].filename, 'wb')
out.write(img["upl_file"].value)
out.close()
request.redirect('/tests')
return ''
你可能不太想去看文档,但我觉得最好的建议还是去看看:
- rfc 2388 从表单返回值:multipart/form-data
- rfc 1867 HTML中的基于表单的文件上传
这样可以确保你不会遗漏任何情况。更简单的方法可能是使用poster这个库。