django:在本地磁盘上注册解压缩的文件

时间:2011-06-17 12:57:09

标签: django django-forms django-file-upload django-storage

[我为这个问题的篇幅提前道歉。]

我在Debian squeeze上使用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)似乎做了我想做的事。

temporary_file_path函数在那里,因为Django File Uploads docs

  

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课程。在_save中,name是相对路径 目的地,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类,位于_file数据成员中。所以 content._file有一个temporary_file_path属性,而不是content 本身。

这是常规文件上传的样子。如你所见,它是相似的。

[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 saveStorage对象。 Django表格 特别是代码非常模糊。

无论如何,我的问题是,在所有这些设置之后,第一个是何时/何时 下面的选项,file_move_safe应该被激活?我 看到这里不匹配。这是一个错误吗?任何人都可以澄清吗?

1 个答案:

答案 0 :(得分:0)

if hasattr(content, 'temporary_file_path') 

由于您声明内容没有temporary_file_path标识符,因此上述条件永远不会与此条件相等。但是,由于content._file可以使用以下内容来获取您正在寻找的功能

if hasattr(content, '_file'):
   if hasattr(content._file,'temporary_file_path'):