Django的上传:放弃上传重复使用现有的文件(基于MD5校验)(Django uploads: D

2019-08-20 08:50发布

我有一个模型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)

任何帮助表示赞赏!

Answer 1:

由于阿尔特斯答案,我能弄清楚,写一个自定义的存储类是关键,这是更容易超出预期。

  • 我只是省略调用父类_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)


Answer 2:

据我所知,你不能轻易实现此使用保存/删除方法怎么把文件都相当特别处理。

但你可以尝试水木清华这样的。

首先,我简单的MD5文件的散列函数:

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

接下来simple_upload_to是水木清华是像你这样的media_file_name功能。 你应该使用这样的:

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)

正如你可以看到这个自定义存储删除保存之前的文件,然后保存新的名称相同。 所以在这里,你可以实现你的逻辑,如果不删除(因此更新)文件是非常重要的。

更多关于储存OU可以在这里找到: https://docs.djangoproject.com/en/1.5/ref/files/storage/



Answer 3:

我有同样的问题,并发现这太问题。 由于这是没有什么太生僻我在网上搜索,发现下面的Python包,其中接缝做的正是你想要什么:

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

如果SHA1哈希值是没问题的,我认为拉请求添加MD5哈希的支持将是一个伟大的想法。



Answer 4:

这个答案帮我解决问题,我想如果所上传的文件已经存在引发异常。 如果具有相同名称的文件在上传位置已经存在,这个版本会引发异常。

from django.core.files.storage import FileSystemStorage

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

    def _save(self, name, content):
        if self.exists(name):
            raise ValidationError('File already exists: %s' % name)

        return super(
            FailOnDuplicateFileSystemStorage, self)._save(name, content)


文章来源: Django uploads: Discard uploaded duplicates, use existing file (md5 based check)