我有一个模型FileField
,其中包含用户上传的文件。 因为我想节省空间,我想避免重复。
我想什么来实现:
- 计算上传的文件的MD5校验
- 存储基于其的md5sum与文件名的文件
- 如果使用该名称的文件已经存在(新文件的副本 ), 丢弃上传的文件,并使用现有的文件,而不是
1和2是已经工作,但我怎么会忘了上传的副本,并使用现有的文件呢?
请注意,我想保持现有的文件 ,而不是覆盖它(主要是为了保持修改时间是相同的-更好的为备份)。
笔记:
- 我使用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)
任何帮助表示赞赏!
由于阿尔特斯答案,我能弄清楚,写一个自定义的存储类是关键,这是更容易超出预期。
- 我只是省略调用父类
_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)
据我所知,你不能轻易实现此使用保存/删除方法怎么把文件都相当特别处理。
但你可以尝试水木清华这样的。
首先,我简单的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/
我有同样的问题,并发现这太问题。 由于这是没有什么太生僻我在网上搜索,发现下面的Python包,其中接缝做的正是你想要什么:
https://pypi.python.org/pypi/django-hashedfilenamestorage
如果SHA1哈希值是没问题的,我认为拉请求添加MD5哈希的支持将是一个伟大的想法。
这个答案帮我解决问题,我想如果所上传的文件已经存在引发异常。 如果具有相同名称的文件在上传位置已经存在,这个版本会引发异常。
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)