Как cgi.FieldStorage хранит файлы? - PullRequest
9 голосов
/ 27 июля 2011

Так что я поигрался с необработанным WSGI, cgi.FieldStorage и загрузкой файлов.И я просто не могу понять, как это происходит с загрузкой файлов.

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

Я попытался прочитать исходный код модуля cgi и нашел кое-что о временных файлах, но он возвращает чертову строку, а не файловый (-подобный) объект!Итак ... как это работает 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 ]

6 голосов
/ 27 июля 2011

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

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

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

Это вернет файлоподобный объект, который доступен для чтения «на досуге».

5 голосов
/ 29 ноября 2011

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

Вы можете использовать некоторое наследование и расширить класс из 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")

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

2 голосов
/ 11 июля 2015

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

#!/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('')

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

if file_field.path:
    # Using an already created file...
else:
    # Creating a temporary named file to store the content.
    import tempfile
    with tempfile.NamedTemporaryFile("w+b", delete=False) as f:
        f.write(file_field.value)
        # You can save the 'f.name' field for later usage.

Если для поля также установлено Content-Length, что, как представляется, редко, файл также должен быть создан с помощью cgi .

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

...