Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/337.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在Django中通过哈希高效地保存文件_Python_Django_Hash - Fatal编程技术网

Python 在Django中通过哈希高效地保存文件

Python 在Django中通过哈希高效地保存文件,python,django,hash,Python,Django,Hash,我正在做一个Django项目。我希望用户能够做的是上传一个文件(通过一个表单),然后将文件本地保存到一个自定义路径,并使用一个自定义文件名——它的散列。我能想到的唯一解决方案是使用我正在使用的文件字段的“upload_to”参数。这意味着(我认为): 1) 将文件写入磁盘 2) 计算散列 3) 返回路径+散列作为文件名 问题在于有两种写入操作:一种是在将文件从内存保存到磁盘以计算散列时,另一种是在实际将文件保存到指定位置时 有没有一种方法可以覆盖FileField的save to disk方法(

我正在做一个Django项目。我希望用户能够做的是上传一个文件(通过一个表单),然后将文件本地保存到一个自定义路径,并使用一个自定义文件名——它的散列。我能想到的唯一解决方案是使用我正在使用的文件字段的“upload_to”参数。这意味着(我认为):

1) 将文件写入磁盘

2) 计算散列

3) 返回路径+散列作为文件名

问题在于有两种写入操作:一种是在将文件从内存保存到磁盘以计算散列时,另一种是在实际将文件保存到指定位置时

有没有一种方法可以覆盖FileField的save to disk方法(或者我在哪里可以准确地找到幕后发生的事情),这样我基本上可以使用临时名称保存文件,然后将其重命名为hash,而不是保存两次


谢谢。

文件字段的
上传到
参数接受一个可调用的参数,从该参数返回的字符串将连接到您的
媒体根目录
设置,以获取最终文件名(从):

这也可能是一个可调用的函数,比如一个函数,它将被调用以获取上传路径,包括文件名。此可调用项必须能够接受两个参数,并返回要传递到存储系统的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])