2010-08-01 3 views
1

Я сделал простой веб-сервер, как показано ниже.Как я могу узнать имя загруженного файла в python cgi

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() 

В методе do_Post из BaseHTTPRequestHandler, я успешно получил загруженные данные файла.

Но я не могу понять, как получить исходное имя загруженного файла. self.rfile.name - это просто «сокет» Как я могу получить имя загруженного файла?

ответ

2

Довольно сломанный код вы используете там в качестве отправной точки (например, смотреть на это global rootnode где имя rootnode используется нигде - явно наполовину отредактированный источник, и плохо в этом).

В любом случае, в какой форме вы используете «клиентскую сторону» для POST? Как установить это поле upfile?

Почему вы не используете обычный подход FieldStorage, как указано в Python's docs? Таким образом, вы можете использовать атрибут .file соответствующего поля, чтобы получить файл-подобный объект для чтения, или его атрибут .value, чтобы прочитать все это в памяти и получить его как строку, плюс атрибут .filename поля, чтобы узнать имя загруженного файла. Более подробные, хотя и сжатые, документы на FieldStorage, являются here.

Edit: теперь, что OP редактировал добротность, чтобы уточнить, я вижу проблему: 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 и его внутренних функциях ;-).

+0

Thnx Alex. Я пробовал cgi.FieldStorage() в методе do_Post(), но он возвращает пустой класс. Должен ли я использовать CGIHTTPRequestHandler с отдельным файлом py, чтобы получить информацию FieldStorage? –

+0

@tk, вы можете это сделать, но нет причин помещать его в отдельный файл '.py'. Если вы отредактируете свой Q, чтобы показать минимальный недостающий код, вместо того, чтобы указывать на этот известный для себя пример, вам будет легче помочь вам; также ** пожалуйста ** отредактируйте свой Q, чтобы показать форму, которую вы используете для загрузки, как я уже упоминал, поскольку совершенно невозможно угадать, какие проблемы у нее могут возникнуть, если вы не будете _show_ it! –

+0

Алекс, спасибо снова. Я исправил свой вопрос с помощью кода python и формы html. –

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() 
+0

в новой версии Python вы должны использовать 'self.headers.get_params()' вместо 'self.headers.getheader()' – maciek

Смежные вопросы