Как работает cgi.FieldStorage хранить файлы?

Итак, я играл с raw WSGI, cgi.FieldStorage и загрузка файлов. И я просто не могу понять, как это связано с загрузкой файлов.

сначала казалось, что он просто хранит весь файл в памяти. И я подумал, что это должно быть легко проверить - большой файл должен забивать память!.. Но этого не произошло. Тем не менее, когда я запрашиваю файл, это строка, а не итератор, файловый объект или что-то еще.

Я попытался прочитать источник модуля cgi и нашел некоторые вещи о временных файлах, но он возвращает долбаную строку, а не объект file(-like)! Так... как это работает fscking?!

вот код, который я использовал:

import cgi
from wsgiref.simple_server import make_server

def app(environ,start_response):
    start_response('200 OK',[('Content-Type','text/html')])
    output = """
    <form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="failas" />
    <input type="submit" value="Varom" />
    </form>
    """
    fs = cgi.FieldStorage(fp=environ['wsgi.input'],environ=environ)
    f = fs.getfirst('failas')
    print type(f)
    return output


if __name__ == '__main__' :
    httpd = make_server('',8000,app)
    print 'Serving'
    httpd.serve_forever()

спасибо заранее! :)

3 ответов


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

если поле представляет загруженный файл, доступ к значению через атрибут value или getvalue() метод читает весь файл в памяти в виде строки. Это может быть не то, чего ты хочешь. Вы можете проверить загруженный файл, проверив атрибут filename или . Тогда вы можете прочитать данные на досуге из атрибута файла:

fileitem = form["userfile"]
if fileitem.file:
    # It's an uploaded file; count lines
    linecount = 0
    while 1:
        line = fileitem.file.readline()
        if not line: break
        linecount = linecount + 1

что касается вашего примера, getfirst() - это просто версия getvalue(). попробуйте заменить

f = fs.getfirst('failas')

С

f = fs['failas'].file

это вернет файловый объект, который читается "на досуге".


лучший способ-не читать файл (или даже каждую строку за раз, как предложил гимел).

вы можете использовать некоторое наследование и расширить класс из FieldStorage, а затем переопределить функцию make_file. make_file вызывается, когда FieldStorage имеет тип file.

для вашей справки, по умолчанию make_file выглядит так:

def make_file(self, binary=None):
    """Overridable: return a readable & writable file.

    The file will be used as follows:
    - data is written to it
    - seek(0)
    - data is read from it

    The 'binary' argument is unused -- the file is always opened
    in binary mode.

    This version opens a temporary file for reading and writing,
    and immediately deletes (unlinks) it.  The trick (on Unix!) is
    that the file can still be used, but it can't be opened by
    another process, and it will automatically be deleted when it
    is closed or when the current process terminates.

    If you want a more permanent file, you derive a class which
    overrides this method.  If you want a visible temporary file
    that is nevertheless automatically deleted when the script
    terminates, try defining a __del__ method in a derived class
    which unlinks the temporary files you have created.

    """
    import tempfile
    return tempfile.TemporaryFile("w+b")

вместо того, чтобы создавать temporaryfile, постоянно создавайте файл везде, где хотите.


используя ответ @hasanatkazmi (используется в скрученном приложении), я получил что-то вроде:

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
# -*- indent: 4 spc -*-
import sys
import cgi
import tempfile


class PredictableStorage(cgi.FieldStorage):
    def __init__(self, *args, **kwargs):
        self.path = kwargs.pop('path', None)
        cgi.FieldStorage.__init__(self, *args, **kwargs)

    def make_file(self, binary=None):
        if not self.path:
            file = tempfile.NamedTemporaryFile("w+b", delete=False)
            self.path = file.name
            return file
        return open(self.path, 'w+b')

имейте в виду, что файл не всегда на cgi модуль. Согласно этим cgi.py строки он будет создан только в том случае, если содержимое превышает 1000 байт:

if self.__file.tell() + len(line) > 1000:
    self.file = self.make_file('')

Итак, вы должны проверить, действительно ли файл был создан с запросом к пользовательскому классу'