pho*_*bos 25 python django django-models django-file-upload
我有一个模型FileField,其中包含用户上传的文件.由于我想节省空间,我想避免重复.
我想要实现的目标:
1和2已经可以使用了,但是如何忘记上传的副本并使用现有文件呢?
请注意,我想保留现有文件而不是覆盖它(主要是为了保持修改时间相同 - 更好的备份).
笔记:
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)
Run Code Online (Sandbox Code Playgroud)
任何帮助表示赞赏!
pho*_*bos 28
感谢alTus的回答,我能够发现编写自定义存储类是关键,它比预期的要容易.
_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)
Run Code Online (Sandbox Code Playgroud)
AFAIK你无法使用保存/删除方法轻松实现这一点.coz文件的处理非常具体.
但你可以尝试这样的smth.
首先,我的简单md5文件哈希函数:
def md5_for_file(chunks):
md5 = hashlib.md5()
for data in chunks:
md5.update(data)
return md5.hexdigest()
Run Code Online (Sandbox Code Playgroud)
接下来simple_upload_to是像你的media_file_name函数一样的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())
Run Code Online (Sandbox Code Playgroud)
当然,这仅仅是一个例子,因此路径生成逻辑可以是多种多样的.
而最重要的部分:
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)
Run Code Online (Sandbox Code Playgroud)
如您所见,此自定义存储在保存之前删除文件,然后保存具有相同名称的新文件.所以在这里你可以实现你的逻辑,如果不删除(并因此更新)文件是重要的.
有关存储的更多信息,请访问:https://docs.djangoproject.com/en/1.5/ref/files/storage/