2

【質問が長くなってしまいましたことをお詫び申し上げます。】

私は Debian スクイーズで Django 1.2.3-3+squeeze1 を使用しています。

zip ファイルをディスクの一時的な場所にアップロードして解凍し、結果を永続的な場所に保存するアプリケーションを作成しています。FileUpload解凍されたファイルは、解凍後に呼び出されるクラスとしてデータベースに登録されます 。アップロードされた圧縮ファイルもクラスに対応していますが、この質問では無視します。FileUploadこのように見えます。

class FileUpload(models.Model):
    folder = models.ForeignKey(FolderUpload, null=True, blank=True, related_name='parentfolder')
    upload_date = models.DateTimeField(default=datetime.now(), blank=True, editable=False)
    upload = models.FileField(upload_to=file_upload_path)
    name = models.CharField(max_length=100)
    description = models.CharField(blank=True, max_length=200)

    def save(self):
        if not self.id:
            if self.folder == None:
                pass
            else:
                self.path = self.folder.path
        super(FileUpload, self).save()

で定義されたフォームも使用しています

from django.forms import ChoiceField, Form, ModelForm

class FileUploadForm(ModelForm):
    class Meta:
        model = FileUpload

ディスク上の解凍されたファイルを取得する関数。それらをデータベースに登録し、 と呼ばれる正しい場所に移動します addFile。以前はこれを使用していました:

def addFile(name, filename, description, content, folder_id=None):
    #f = open(filename, 'r')
    #content = f.read()
    from forms import FileUploadForm
    from django.core.files.uploadedfile import SimpleUploadedFile
    if folder_id == None:
        data = {'name':name, 'description':description}
    else:
        data = {'name':name, 'description':description, 'folder':str(folder_id)}
    file_data = {'upload': SimpleUploadedFile(filename, content)}
    ff = FileUploadForm(data, file_data)
    try:
        zf = ff.save(commit=False)
        zf.save()
    except:
        raise RuntimeError, "Form error is %s."%(ff.errors)
    return zf

これは機能しましたが、問題はファイル全体をメモリにダンプすることでした。大きなファイルの場合、特に Python がメモリの経済性で知られていないことを考えると、これは大量のメモリを消費していました。だから私はこれに切り替えました:

from django.core.files.uploadedfile import UploadedFile

class UnzippedFile(UploadedFile):

    def __init__(self, file, filepath, content_type='text/plain', charset=None):
        import os
        self.filepath = filepath
        self.name = os.path.basename(filepath)
        self.size = os.path.getsize(filepath)
        self.content_type = content_type
        self.charset = charset
        super(UnzippedFile, self).__init__(file, self.name, content_type, self.size, charset)

    def temporary_file_path(self):
        """
        Returns the full path of this file.
        """
        return self.filepath

def addFile(filepath, description, file, folder_id=None):
    import os, sys
    from forms import FileUploadForm
    from django.core.files.uploadedfile import UploadedFile
    name = os.path.basename(filepath)
    if folder_id == None:
        data = {'name':name, 'description':description}
    else:
        data = {'name':name, 'description':description, 'folder':str(folder_id)}
    file_data = {'upload': UnzippedFile(file, filepath)}
    ff = FileUploadForm(data, file_data)
    try:
        zf = ff.save(commit=False)
        zf.save()
    except:
        raise
    return zf

UploadedFile( に) 既に存在していた派生クラスはどれも、 django/core/files/uploadedfile.py私が望んでいたことをしていないように見えたので、私は をサブクラス化することを余儀なくされました。

Django File Uploads docsが言うので、temporary_file_path関数はそこにあります

UploadedFile.temporary_file_path()

ディスクにアップロードされたファイルのみがこのメソッドを持ちます。アップロードされた一時ファイルへのフル パスを返します。

後述するように、FileSystemStorageクラスは関数内でこの属性を探して いるようです。_save

nが zip アーカイブ内のファイルの相対パスである場合、使用法は

name = os.path.normpath(os.path.basename(n)) # name of file
pathname = os.path.join(dirname, n) # full file path
description = "from zip file '" + zipfilename + "'" # `zipfilename` is the name of the zip file
fname = open(pathname) # file handle
f = addFile(pathname, description, fname)

これは機能しますが、コードをトレースしたところ、コードがストリーミングを使用していることがわかりました。この場合、ファイルを一時的な場所から永続的な場所にコピーすることが明らかに最適です。問題のコードは 、クラスdjango/core/files/storage.py_save関数にあり ます。FileSystemStorage_savenameは宛先の相対パスで、contentFileオブジェクトです。

def _save(self, name, content):
    full_path = self.path(name)
     directory = os.path.dirname(full_path)
     [...]
    while True:
            try:
                # This file has a file path that we can move.
                if hasattr(content, 'temporary_file_path'):
                    file_move_safe(content.temporary_file_path(), full_path)
                    content.close()

                # This is a normal uploadedfile that we can stream.
                else:
                    # This fun binary flag incantation makes os.open throw an
                    # OSError if the file already exists before we open it.
                    fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
                    try:
                        locks.lock(fd, locks.LOCK_EX)
                        for chunk in content.chunks():
                            os.write(fd, chunk)
                    finally:
                        locks.unlock(fd)
                        os.close(fd)
            except OSError, e:
                if e.errno == errno.EEXIST:
                    # Ooops, the file exists. We need a new file name.
                    name = self.get_available_name(name)
                    full_path = self.path(name)
                else:
                    raise
            else:
                # OK, the file save worked. Break out of the loop.
                break

関数は_save属性 を探しています temporary_file_path。このコードは、 でtemporary_file_path前述した関数 によってトリガーされることを意図していると思いますdjango/core/files/uploadedfile.py。ただし、実際に渡される (content引数に対応する) クラスはであり、このオブジェクト<class 'django.db.models.fields.files.FieldFile'>の属性 dict ( ) は次のようになります。content.__dict__

{'_committed': False, 'name': u'foo', 'instance': <FileUpload: foo>,
'_file': <UnzippedFile: foo (text/plain)>, 'storage':<django.core.files.storage.DefaultStorage object at 0x9a70ccc>,
'field': <django.db.models.fields.files.FileField object at0x9ce9b4c>, 'mode': None}

は、データ メンバー内にあるクラスにtemporary_file_pathアタッチされ ます。その ため、それ自体ではなく、属性があり ます。UnzippedFile_filecontent._filetemporary_file_pathcontent

これは、通常のファイルのアップロードがどのように見えるかです。ご覧のとおり、似ています。

[Fri Jun 17 08:05:33 2011] [error] type of content is <class 'django.db.models.fields.files.FieldFile'>
[Fri Jun 17 08:05:33 2011] [error] {'_committed': False, 'name': u'behavior.py',
                                    'instance': <FileUpload: b>, '_file': <TemporaryUploadedFile: behavior.py (text/x-python)>,
                                    'storage': <django.core.files.storage.DefaultStorage object at 0xb8d7fd8c>,
                                    'field': <django.db.models.fields.files.FileField object at 0xb8eb584c>, 'mode': None}

FileUploadForm saveコードがからStorageオブジェクトに到達する方法を詳細に追跡することは困難です。特に Django フォームのコードはかなりあいまいです。

とにかく、私の質問は、このすべてのセットアップの後、以下の最初のオプションがどのように/いつfile_move_safeアクティブになるはずですか? ここにミスマッチが見られます。これはバグですか?誰でも明確にできますか?

4

1 に答える 1

0
if hasattr(content, 'temporary_file_path') 

コンテンツに temporary_file_path 識別子がないことを述べているため、上記はこの条件付きでは決して真になりません。ただし、 content._file は次のことを行うため、探している機能を取得するために次を使用できます

if hasattr(content, '_file'):
   if hasattr(content._file,'temporary_file_path'):
于 2011-06-17T20:57:42.033 に答える