Django上载:丢弃上载的重复项,使用现有文件(基于md5的检查)

2024-04-24 10:23:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个带FileField的模型,它保存用户上传的文件。既然我想节省空间,我想避免重复。

我希望实现的目标:

  1. 计算上载的文件md5校验和
  2. 根据md5sum存储文件名为的文件
  3. 如果已经存在具有该名称的文件(新文件是重复的文件),放弃上载的文件并使用现有文件

12已经可以工作了,但是我如何才能忘记上传的副本并使用现有文件?

请注意,我希望保留现有文件并且覆盖它(主要是保持修改的时间不变-更好地用于备份)。

注意:

  • 我用的是Django 1.5
  • 上载处理程序是django.core.files.uploadhandler.TemporaryFileUploadHandler

代码:

def media_file_name(instance, filename):
    h = instance.md5sum
    basename, ext = os.path.splitext(filename)
    return os.path.join('mediafiles', h[0:1], h[1:2], h + ext.lower())

class Media(models.Model):
    orig_file = models.FileField(upload_to=media_file_name)
    md5sum = models.CharField(max_length=36)
    ...

    def save(self, *args, **kwargs):
            if not self.pk:  # file is new
                md5 = hashlib.md5()
                for chunk in self.orig_file.chunks():
                    md5.update(chunk)
                self.md5sum = md5.hexdigest()
            super(Media, self).save(*args, **kwargs)

感谢您的帮助!


Tags: 文件pathinstancenameselfosmodelsdef
3条回答

不过,使用save/delete方法很难实现这一点,因为z文件的处理非常具体。

但你可以试试这样的短信。

首先,我的简单md5文件哈希函数:

def md5_for_file(chunks):
    md5 = hashlib.md5()
    for data in chunks:
        md5.update(data)
    return md5.hexdigest()

下一个simple_upload_to是smth,类似于您的媒体文件名函数。 你应该这样使用它:

def simple_upload_to(field_name, path='files'):
    def upload_to(instance, filename):
        name = md5_for_file(getattr(instance, field_name).chunks())
        dot_pos = filename.rfind('.')
        ext = filename[dot_pos:][:10].lower() if dot_pos > -1 else '.unknown'
        name += ext
        return os.path.join(path, name[:2], name)
    return upload_to

class Media(models.Model):
    # see info about storage below
    orig_file = models.FileField(upload_to=simple_upload_to('orig_file'), storage=MyCustomStorage())

当然,这只是一个例子,因此路径生成逻辑可以是多种多样的。

最重要的是:

from django.core.files.storage import FileSystemStorage

class MyCustomStorage(FileSystemStorage):
    def get_available_name(self, name):
        return name

    def _save(self, name, content):
        if self.exists(name):
            self.delete(name)
        return super(MyCustomStorage, self)._save(name, content)

如您所见,此自定义存储在保存之前删除文件,然后用相同的名称保存新文件。 因此,如果不删除(并因此更新)文件很重要,可以在这里实现逻辑。

有关存储的详细信息,请访问:https://docs.djangoproject.com/en/1.5/ref/files/storage/

多亏了alTus的回答,我才发现写一个custom storage class是关键,而且比预期的要容易。

  • 我只是省略了调用超类_save方法来编写文件(如果它已经存在)并返回名称。
  • 我覆盖get_available_name,以避免在已经存在同名文件时在文件名后面附加数字

我不知道这是不是做这件事的正确方法,但到目前为止效果还不错。

希望这是有用的!

以下是完整的示例代码:

import hashlib
import os

from django.core.files.storage import FileSystemStorage
from django.db import models

class MediaFileSystemStorage(FileSystemStorage):
    def get_available_name(self, name, max_length=None):
        if max_length and len(name) > max_length:
            raise(Exception("name's length is greater than max_length"))
        return name

    def _save(self, name, content):
        if self.exists(name):
            # if the file exists, do not call the superclasses _save method
            return name
        # if the file is new, DO call it
        return super(MediaFileSystemStorage, self)._save(name, content)


def media_file_name(instance, filename):
    h = instance.md5sum
    basename, ext = os.path.splitext(filename)
    return os.path.join('mediafiles', h[0:1], h[1:2], h + ext.lower())


class Media(models.Model):
    # use the custom storage class fo the FileField
    orig_file = models.FileField(
        upload_to=media_file_name, storage=MediaFileSystemStorage())
    md5sum = models.CharField(max_length=36)
    # ...

    def save(self, *args, **kwargs):
        if not self.pk:  # file is new
            md5 = hashlib.md5()
            for chunk in self.orig_file.chunks():
                md5.update(chunk)
            self.md5sum = md5.hexdigest()
        super(Media, self).save(*args, **kwargs)

我有同样的问题,发现这个问题。由于这并不罕见,我在网上搜索了一下,找到了下面的Python包,它可以完全按照您的要求运行:

https://pypi.python.org/pypi/django-hashedfilenamestorage

如果SHA1散列是不可能的,我认为添加MD5散列支持的pull请求将是一个好主意。

相关问题 更多 >