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)中的派生类似乎都没有做我想做的事。

temporary_file_path功能在那里,因为Django File Uploads 文档

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是目的地的相对路径,content是一个File对象。

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'>,这是content.__dict__该对象的属性 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 save传递到Storage对象的。特别是 Django 表单代码非常晦涩难懂。

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

4

1 回答 1

0
if hasattr(content, 'temporary_file_path') 

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

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