Python 在Django中通过哈希高效地保存文件
我正在做一个Django项目。我希望用户能够做的是上传一个文件(通过一个表单),然后将文件本地保存到一个自定义路径,并使用一个自定义文件名——它的散列。我能想到的唯一解决方案是使用我正在使用的文件字段的“upload_to”参数。这意味着(我认为): 1) 将文件写入磁盘 2) 计算散列 3) 返回路径+散列作为文件名 问题在于有两种写入操作:一种是在将文件从内存保存到磁盘以计算散列时,另一种是在实际将文件保存到指定位置时 有没有一种方法可以覆盖FileField的save to disk方法(或者我在哪里可以准确地找到幕后发生的事情),这样我基本上可以使用临时名称保存文件,然后将其重命名为hash,而不是保存两次Python 在Django中通过哈希高效地保存文件,python,django,hash,Python,Django,Hash,我正在做一个Django项目。我希望用户能够做的是上传一个文件(通过一个表单),然后将文件本地保存到一个自定义路径,并使用一个自定义文件名——它的散列。我能想到的唯一解决方案是使用我正在使用的文件字段的“upload_to”参数。这意味着(我认为): 1) 将文件写入磁盘 2) 计算散列 3) 返回路径+散列作为文件名 问题在于有两种写入操作:一种是在将文件从内存保存到磁盘以计算散列时,另一种是在实际将文件保存到指定位置时 有没有一种方法可以覆盖FileField的save to disk方法(
谢谢。文件字段的
上传到
参数接受一个可调用的参数,从该参数返回的字符串将连接到您的媒体根目录
设置,以获取最终文件名(从):
这也可能是一个可调用的函数,比如一个函数,它将被调用以获取上传路径,包括文件名。此可调用项必须能够接受两个参数,并返回要传递到存储系统的Unix样式的路径(带正斜杠)。将要传递的两个参数是:
:定义文件字段的模型实例。更具体地说,这是附加当前文件的特定实例。在大多数情况下,此对象尚未保存到数据库中,因此如果它使用默认的自动字段,则其主键字段可能还没有值李>instance
:最初指定给文件的文件名。在确定最终目的地路径时,可以考虑也可以不考虑这一点李>filename
model.my_file_字段时,它将解析为的实例,其作用类似于文件。因此,您应该能够编写一个upload\u到
,如下所示:
def hash_上传(实例,文件名):
instance.my_file.open()#确保我们位于文件的开头
contents=instance.my_file.read()#获取内容
fname,ext=os.path.splitext(文件名)
返回“{0}{1}{2}”。格式化(fname,hash_函数(contents),ext)#组装文件名
替换要使用的适当哈希函数。根本不需要保存到磁盘(事实上,文件通常已上载到临时存储器,或者较小的文件仅保存在内存中)
您可以这样使用:
class MyModel(models.Model):
my_file = models.FileField(upload_to=hash_upload,...)
我还没有对此进行测试,因此您可能需要戳一下读取整个文件的行(并且您可能只想散列文件的第一部分,以防止恶意用户上载大量文件并导致DoS攻击)。您可以使用
instance.my\u file.read(instance.my\u file.DEFAULT\u CHUNK\u SIZE)
更新了至少1.10的答案:
- 您的
实例。我的文件\u字段
是的实例,而不是类似文件的对象
- 它不能打开或关闭,只能读取,也可能是分块读取
- 无条件调用read()可能会消耗所有可用的物理内存
在下面的示例中,该实例有一个类方法“get_image_basedir”,因为有几个模型都使用相同的函数,但需要不同的基本目录。我忘了,因为这是一种常见的模式。
HASH_CHUNK_SIZE是我自己设置的变量,用于优化磁盘读取(即匹配文件系统的块大小或其倍数)
这就是我正在做的,但我没有将文件视为已经写入磁盘,而是保存它(根据文档,逐块),然后计算散列。我不知道我可以将其视为已保存。当该字段不包含图像时,此方法似乎不起作用。在这种情况下,哈希似乎是从以前的内容派生的,而不是新的内容。查看save
方法显示,在调用generate\u filename
之前,内容未绑定到FieldFile
。不知道如何从上传函数中获取内容的哈希值,然后。。。最好的选择可能是子类化FieldFile::save
。
def get_image_path(instance, filename):
import os.path
import hashlib
base = instance.get_image_basedir()
parts = os.path.splitext(filename)
ctx = hashlib.sha256()
if instance.img.multiple_chunks():
for data in instance.img.chunks(HASH_CHUNK_SIZE):
ctx.update(data)
else:
ctx.update(instance.img.read())
return os.path.join(base, ctx.hexdigest() + parts[1])