【问题标题】:how can i find out the uploaded file name in python cgi如何在 python cgi 中找到上传的文件名
【发布时间】:2018-11-22 15:23:48
【问题描述】:

我制作了如下所示的简单网络服务器。

import BaseHTTPServer, os, cgi
import cgitb; cgitb.enable()

html = """
<html>
<body>
<form action="" method="POST" enctype="multipart/form-data">
File upload: <input type="file" name="upfile">
<input type="submit" value="upload">
</form>
</body>
</html>
"""
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("content-type", "text/html;charset=utf-8")
        self.end_headers()
        self.wfile.write(html)

    def do_POST(self):
        ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
        if ctype == 'multipart/form-data':
            query = cgi.parse_multipart(self.rfile, pdict)
            upfilecontent = query.get('upfile')
            if upfilecontent:
                # i don't know how to get the file name.. so i named it 'tmp.dat'
                fout = file(os.path.join('tmp', 'tmp.dat'), 'wb')
                fout.write (upfilecontent[0])
                fout.close()
        self.do_GET()

if __name__ == '__main__':
    server = BaseHTTPServer.HTTPServer(("127.0.0.1", 8080), Handler)
    print('web server on 8080..')
    server.serve_forever()

在BaseHTTPRequestHandler的do_Post方法中,我成功获取了上传的文件数据。

但我不知道如何获取上传文件的原始名称。 self.rfile.name 只是一个“套接字” 如何获取上传的文件名?

【问题讨论】:

    标签: python cgi


    【解决方案1】:

    通过使用 cgi.FieldStorage,您可以轻松提取文件名。检查以下示例:

    def do_POST(self):
        ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
        if ctype == 'multipart/form-data':
            form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], })
            filename = form['upfile'].filename
            data = form['upfile'].file.read()
            open("./%s"%filename, "wb").write(data)
        self.do_GET()
    

    【讨论】:

    • 在较新的 Python 版本中,您应该使用 self.headers.get_params() 而不是 self.headers.getheader()
    【解决方案2】:

    你在那里使用的代码很糟糕(例如,看看global rootnode,其中使用了名称rootnode无处——显然是半编辑的源代码,而且很糟糕)。

    无论如何,POST 使用“客户端”的形式是什么? upfile 字段是如何设置的?

    您为什么不使用Python's docs 中记录的普通FieldStorage 方法?这样,您可以使用相应字段的.file 属性来获取要读取的类文件对象,或者使用其.value 属性将其全部读取到内存中并将其作为字符串获取,再加上.filename 属性的字段知道上传文件的名称。 FieldStorage 上的更详细但简洁的文档是 here

    编辑:既然 OP 已经编辑了 Q 来澄清,我看到了问题:BaseHTTPServer 确实 根据 CGI 规范设置环境,所以cgi 模块不太适用。不幸的是,唯一简单的环境设置方法是从CGIHTTPServer.py 窃取和破解一大段代码(不打算重用,因此需要、叹息、复制和粘贴编码),例如......:

    def populenv(self):
            path = self.path
            dir, rest = '.', 'ciao'
    
            # find an explicit query string, if present.
            i = rest.rfind('?')
            if i >= 0:
                rest, query = rest[:i], rest[i+1:]
            else:
                query = ''
    
            # dissect the part after the directory name into a script name &
            # a possible additional path, to be stored in PATH_INFO.
            i = rest.find('/')
            if i >= 0:
                script, rest = rest[:i], rest[i:]
            else:
                script, rest = rest, ''
    
            # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html
            # XXX Much of the following could be prepared ahead of time!
            env = {}
            env['SERVER_SOFTWARE'] = self.version_string()
            env['SERVER_NAME'] = self.server.server_name
            env['GATEWAY_INTERFACE'] = 'CGI/1.1'
            env['SERVER_PROTOCOL'] = self.protocol_version
            env['SERVER_PORT'] = str(self.server.server_port)
            env['REQUEST_METHOD'] = self.command
            uqrest = urllib.unquote(rest)
            env['PATH_INFO'] = uqrest
            env['SCRIPT_NAME'] = 'ciao'
            if query:
                env['QUERY_STRING'] = query
            host = self.address_string()
            if host != self.client_address[0]:
                env['REMOTE_HOST'] = host
            env['REMOTE_ADDR'] = self.client_address[0]
            authorization = self.headers.getheader("authorization")
            if authorization:
                authorization = authorization.split()
                if len(authorization) == 2:
                    import base64, binascii
                    env['AUTH_TYPE'] = authorization[0]
                    if authorization[0].lower() == "basic":
                        try:
                            authorization = base64.decodestring(authorization[1])
                        except binascii.Error:
                            pass
                        else:
                            authorization = authorization.split(':')
                            if len(authorization) == 2:
                                env['REMOTE_USER'] = authorization[0]
            # XXX REMOTE_IDENT
            if self.headers.typeheader is None:
                env['CONTENT_TYPE'] = self.headers.type
            else:
                env['CONTENT_TYPE'] = self.headers.typeheader
            length = self.headers.getheader('content-length')
            if length:
                env['CONTENT_LENGTH'] = length
            referer = self.headers.getheader('referer')
            if referer:
                env['HTTP_REFERER'] = referer
            accept = []
            for line in self.headers.getallmatchingheaders('accept'):
                if line[:1] in "\t\n\r ":
                    accept.append(line.strip())
                else:
                    accept = accept + line[7:].split(',')
            env['HTTP_ACCEPT'] = ','.join(accept)
            ua = self.headers.getheader('user-agent')
            if ua:
                env['HTTP_USER_AGENT'] = ua
            co = filter(None, self.headers.getheaders('cookie'))
            if co:
                env['HTTP_COOKIE'] = ', '.join(co)
            # XXX Other HTTP_* headers
            # Since we're setting the env in the parent, provide empty
            # values to override previously set values
            for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
                      'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'):
                env.setdefault(k, "")
            os.environ.update(env)
    

    这可以进一步大大简化,但不能不花一些时间和精力在该任务上:-(。

    有了这个populenv函数,我们可以重新编码:

    def do_POST(self):
        populen(self)
        form = cgi.FieldStorage(fp=self.rfile)
        upfilecontent = form['upfile'].value
        if upfilecontent:
            fout = open(os.path.join('tmp', form['upfile'].filename), 'wb')
            fout.write(upfilecontent)
            fout.close()
        self.do_GET()
    

    ...从此过上幸福的生活;-)。 (当然,使用任何像样的 WSGI 服务器,甚至 the demo one 都会容易得多,但是这个练习对 CGI 及其内部结构很有启发性;-)。

    【讨论】:

    • 谢谢亚历克斯。我在 do_Post() 方法中尝试了 cgi.FieldStorage() ,但它返回空类。我应该使用 CGIHTTPRequestHandler 和单独的 py 文件来获取 FieldStorage 信息吗?
    • @tk,您可以这样做,但没有理由将其放在单独的 .py 文件中。如果你编辑你的 Q 以显示最少量的失败代码,而不是指向那个已知被破坏的例子,那么任何人都会更容易帮助你;还编辑您的 Q 以显示您用于上传的表单,正如我已经提到的,因为除非您 显示 它,否则完全不可能猜测它可能有什么问题!
    • Alex,再次感谢。我用 python 代码和表单 html 解决了我的问题。
    【解决方案3】:

    ...或使用您自己的 cgi.parse_multipart 版本,特别是修复此问题:

        # my fix: prefer 'filename' over 'name' field!
        if 'filename' in params:
            name = params['filename']
            name = os.path.basename(name) # Edge, IE return abs path!
        elif 'name' in params:
            name = params['name']
        else:
            continue
    

    【讨论】:

      猜你喜欢
      • 2011-06-20
      • 2012-10-27
      • 2014-07-06
      • 1970-01-01
      • 2017-03-09
      • 2013-02-10
      • 2015-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多