如何获取上传文件的文件大小和SHA-1摘要?

4 投票
3 回答
3838 浏览
提问于 2025-04-17 10:48

我在我的Django应用里有一个模型:

class Image(models.Model):
    image_file = models.ImageField(
        upload_to='images/', 
        width_field='width',
        height_field='height'
    )
    width = models.PositiveIntegerField(
        blank = True, null = True,
        editable = False
    )
    height = models.PositiveIntegerField(
        blank = True, null = True,
        editable = False
    )

    sha1 = models.CharField(max_length=32, blank=True, editable=False)
    filesize = models.PositiveIntegerField(blank=True, null=True, editable=False)

现在我可以通过Django的管理网站上传图片了。而且,当图片上传时,width(宽度)和height(高度)这两个属性会自动保存在数据库里,这是因为使用了特别的ImageField参数。

不过,我还想让它自动计算上传文件的大小SHA-1摘要,并把这些属性也保存下来。我该怎么做呢?

3 个回答

1

我不太确定你是否能自动完成这个操作。不过,ImageField其实也是一种FileField,所以你可以打开这个文件,然后用hashlib.sha1来计算它的校验和。为了计算校验和,你需要读取文件,这样你也可以同时获取文件的大小。

我已经有一段时间没用Django的ORM了,但我记得可以写一个方法,这个方法会在模型实例被保存到存储或者从存储读取时被调用。这个地方就是进行计算的好时机。

4

虽然Burhan Khalid已经给出了答案,但我觉得这只是解决这个问题的一部分。它仍然没有解决保存到数据库的部分。这里有一个完整的解决方案,它还使用了更新的with语句,这样可以利用Python和Django的文件context_manager(所以不需要手动调用file.close(),这个过程会自动完成):

import hashlib
class Image(models.Model):
#...
def save(self, *args, **kwargs):
    with self.image_file.open('rb') as f:
        hash = hashlib.sha1()
        if f.multiple_chunks():
        for chunk in f.chunks():
            hash.update(chunk)
        else:    
            hash.update(f.read())
        self.sha1 =  hash.hexdigest()
        self.filesize = self.image_file.size 
        super(Image, self).save(*args, **kwargs)

请注意,super()是在with语句内部调用的。这一点很重要,否则你会遇到一个错误:ValueError: I/O operation on closed file. 这个错误是因为Django试图读取一个已经关闭的文件,结果它以为这个文件还是打开的。super()也是最后一个命令,用来把我们更新的所有内容保存到数据库中(在之前的最佳答案中,这一步被遗漏了,你可能需要再次调用save()才能真正保存这些细节)。

4

过了一段时间,但像这样的代码应该可以正常工作:

import hashlib
class Image(models.Model):
#...
    def save(self, *args, **kwargs):
        super(Image, self).save(*args, **kwargs)
        f = self.image_file.open('rb')
        hash = hashlib.sha1()
        if f.multiple_chunks():
           for chunk in f.chunks():
              hash.update(chunk)
        else:    
              hash.update(f.read())
        f.close()
        self.sha1 =  hash.hexdigest()
        self.filesize = self.image_file.size 

编辑: 根据Dan的建议,增加了按块读取的功能。默认的块大小是64KB。

撰写回答