如何使用Django ImageField,以及为什么要使用它?

时间:2016-05-20 01:30:33

标签: django django-models

到目前为止,我一直将我的图像文件名存储在CharField中,并将实际文件直接保存到S3。这对我自己的用法来说是个很好的解决方案。我想重新考虑使用ImageField,因为现在将有其他用户和文件输入验证是合适的。

我在阅读the docsthe source code后面找到FileField(看起来基本上是ImageField减去枕头检查和维度字段更新功能)时,有几个问题没有得到完全解答。

1)为什么要使用ImageField?或者更确切地说,为什么要使用FileField?当然,它方便快捷方式,便于插入Django模板。但是有没有实质性的原因,例如。它是否明显受到攻击和恶意上传的保护?

2)如何写入字段文件?如果instance.imagefield(或者是instance.imagefield.file?)读取文件是否正确,如果我想写信给我,我可以简单地执行以下操作吗?

@receiver(pre_save, sender=Image)
def pre_save_image(sender, instance, *args, **kwargs):
    instance.imagefield = process_image(instance.imagefield)

3)如何尝试使用特定文件名保存,如果随机生成的文件名已经存在,请使用新文件名重试?例如我现在用我的代码执行此操作,如何使用ImageField完成?我想在模型层执行此操作,因为如果我在视图层重复尝试,那么pre_save处理将再次运行,这是贫民窟(即使它不太可能有它会有在服务的一生中第二次尝试。)

for i in range(tries):
    try:
        name = generate_random_name()
        media_storage.save(name + '.jpg', ContentFile(final_bytes))
        break
    except:
        pass

4)在models.py pre_savepost_save信号以及实际模型的save()中,如何判断文件是否包含在请求中?即我想知道是否要保存新图像,或者是否没有图像(对象中的某些其他字段正在更新,图像本身保持不变)。

3 个答案:

答案 0 :(得分:6)

我没有看到FileField或ImageField在今天你所做的事情上有什么优势。事实上,正如我所看到的,处理上传的正确/现代/可扩展方式是让客户端(浏览器)直接将文件上传到S3。

如果操作正确(从安全角度来看),此方案允许您以令人难以置信的方式进行扩展,而无需在您身边添加更多计算机电源。例如,考虑100个人同时上传图片。您的服务器需要接收所有这些数据,只能将其再次上传到S3。另一方面,您可以同时上传1000人,我可以向您保证AWS可以处理它。您的服务器只需要处理URL的签名,这是一项很少的工作。

看看精细上传器,作为一种很好的技术,用于处理有效上传到s3(加载块,错误检查等):http://docs.fineuploader.com/endpoint_handlers/amazon-s3.html。 Google" django fineuploader"找到Django的示例应用程序。

就我而言,我使用的模型包含一对CharFieldsbucketkey)以及一些特定于我的应用程序的其他内容。我的数据流如下:

  • Django使用精细上传器小部件为页面提供服务,并根据我的设置进行配置。
  • Fineuploader从django服务器(端点)请求签名的URL,并使用它直接上传到S3。
  • 当上传完成后,fineUploader向我的服务器发出另一个请求以注册上传完成,此时,我在数据库上创建了我的对象。在这种情况下,如果上传失败,我从不在数据库上创建对象。
  • 在AWS端,S3触发​​Lambda函数,我用它来创建缩略图,并将其存储回S3。所以,我甚至不使用我自己的CPU(例如Celery)来调整大小。所以你看,我不仅可以同时上传数千名用户,而且我可以并行调整这千张图片的大小,并且比EC2工作人员花费的费用还少。
  • 我的Django模型也用作管理业务逻辑的包装器(例如get_original_url()get_thumbnail_url()等函数),因此在上传之后,我的模板很容易获得签名读取-onlly URL。

简而言之,您可以根据需要实施自己的Fineuploader版本,或者使用许多替代方案,但假设您遵循AWS方面建议的安全最佳实践(例如,创建一个仅具有写入权限的特殊IAM客户端,即使您使用的是签名URL),这个IMO也是处理上传的最佳方法,尤其是当您使用S3或类似文件来存储这些文件时。

很抱歉,如果我只回答问题1,但如果您接受我的答案1,则问题2和3不适用。

答案 1 :(得分:3)

1)为什么要使用ImageField?或者更确切地说,为什么要使用FileField?

  

方便快捷方便,便于插入   到Django模板。

但是有没有实质性的原因,例如。它是否明显受到攻击和恶意上传的保护?

是。我敢说你自己的代码也可能会这样做,但对于使用FileField的newby可能会确保你的重要系统文件不会被恶意上传覆盖。

2)如何写入字段文件?

在您的情况下,您需要使用特殊的存储后端,以便直接写入Amazon S3。如您所知,FileFile和ImageField的存储后端是可插入的。这是一个示例插件:`http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html

有示例代码,演示如何写入。所以我不会进入那个。

3)如何尝试使用特定文件名保存,如果随机生成的文件名已经存在,请使用新文件名再次尝试?

ImageField和FileField会自动为您解决此问题。如果旧的文件名存在,它将创建一个新的文件名。当我一遍又一遍地调用它时,我的回答here中的代码自动完成了。这里有一些示例文件名产生(输入是bada.png)

"4", "media/bada.png"
"5", "media/bada_aH0gV7t.png"
"7", "media/bada_XkzthgK.png"
"8", "media/bada_YzZuwDi.png"
"9", "media/bada_wpkasI3.png"

4)在models.py pre_save和post_save信号以及实际模型的save()中,如何判断文件是否带有请求?

您的instance.pk将为无

如果这是对现有文件的修改,则将设置PK。

如果这是pre_save

中的新图片上传

答案 2 :(得分:0)

让我永远学习如何使用ImageField保存图像。事实证明这很容易 - 一旦你知道怎么做,它至少就是这样。我的意思是,在你看到之后,这一切都明显地结合在一起。

所以基本上,你正在使用FileField。我已经研究过ImageField和FileField之间的区别:

  • ImageField接受FileField在属性方面的所有内容, 但是如果指示,ImageField也会采用width和height属性。 与FileField不同,ImageField验证上传,确保上传 一个图像。

使用ImageField归结为大多数与FileField相同的结构。最值得记住的事情:

request.FILES['name_of_model']

因此,form.py(或您的表单所在的位置)中的某些内容会生成一个表单,如下所示:

imgfile         =   forms.ImageField(label = 'Choose your image',  
                                          help_text = 'The image should be cool.')

在模型中,你可能会有这样的信息:

imgfile = models.ImageField(upload_to =' images /%m /%d') 因此,将有来自用户的POST请求(当用户完成表单时)。该请求基本上包含数据字典。字典保存提交的文件。要将请求集中在字段中的文件(在我们的示例中为ImageField),您可以使用:

request.FILES['imgfield']

在构造模型对象(实例化模型类)时,可以使用它:

newPic = ImageModel(imgfile = request.FILES['imgfile'])

要以简单的方式保存,您只需使用赋予对象的save()方法(因为Django非常棒):

if form.is_valid():  
        newPic = Pic(imgfile = request.FILES['imgfile'])
        newPic.save()

默认情况下,您的图像将存储到您在settings.py中为MEDIA_ROOT指定的目录。

当你遇到困难时,这个难以捉摸的部分正在访问图像。

在您的模板中,您可以使用以下内容:

<img src="{{ MEDIA_URL }}{{ image.imgfile.name }}"></img>

其中{{MEDIA_URL}}类似于/ media /,如settings.py和{{image.imgfile.name}}所示,是您在模型中指明的文件名和子目录。 &#34;图像&#34;在这种情况下,只是您可能创建的图像循环中的当前图像,以访问数据库中的每个图像:

{%for images in images%}
            

  •         {%endfor%}

    确保正确配置您的网址以处理图片或图片无法正常工作。将其添加到您的网址:

    urlpatterns += patterns('',  
            url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
                'document_root': settings.MEDIA_ROOT,
            }),
       )