到目前为止,我一直将我的图像文件名存储在CharField中,并将实际文件直接保存到S3。这对我自己的用法来说是个很好的解决方案。我想重新考虑使用ImageField,因为现在将有其他用户和文件输入验证是合适的。
我在阅读the docs和the 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_save
和post_save
信号以及实际模型的save()
中,如何判断文件是否包含在请求中?即我想知道是否要保存新图像,或者是否没有图像(对象中的某些其他字段正在更新,图像本身保持不变)。
答案 0 :(得分:6)
我没有看到FileField或ImageField在今天你所做的事情上有什么优势。事实上,正如我所看到的,处理上传的正确/现代/可扩展方式是让客户端(浏览器)直接将文件上传到S3。
如果操作正确(从安全角度来看),此方案允许您以令人难以置信的方式进行扩展,而无需在您身边添加更多计算机电源。例如,考虑100个人同时上传图片。您的服务器需要接收所有这些数据,只能将其再次上传到S3。另一方面,您可以同时上传1000人,我可以向您保证AWS可以处理它。您的服务器只需要处理URL的签名,这是一项很少的工作。
看看精细上传器,作为一种很好的技术,用于处理有效上传到s3(加载块,错误检查等):http://docs.fineuploader.com/endpoint_handlers/amazon-s3.html。 Google" django fineuploader"找到Django的示例应用程序。
就我而言,我使用的模型包含一对CharFields
(bucket
,key
)以及一些特定于我的应用程序的其他内容。我的数据流如下:
get_original_url()
和get_thumbnail_url()
等函数),因此在上传之后,我的模板很容易获得签名读取-onlly URL。简而言之,您可以根据需要实施自己的Fineuploader版本,或者使用许多替代方案,但假设您遵循AWS方面建议的安全最佳实践(例如,创建一个仅具有写入权限的特殊IAM客户端,即使您使用的是签名URL),这个IMO也是处理上传的最佳方法,尤其是当您使用S3或类似文件来存储这些文件时。
很抱歉,如果我只回答问题1,但如果您接受我的答案1,则问题2和3不适用。
答案 1 :(得分:3)
方便快捷方便,便于插入 到Django模板。
但是有没有实质性的原因,例如。它是否明显受到攻击和恶意上传的保护?
是。我敢说你自己的代码也可能会这样做,但对于使用FileField的newby可能会确保你的重要系统文件不会被恶意上传覆盖。
在您的情况下,您需要使用特殊的存储后端,以便直接写入Amazon S3。如您所知,FileFile和ImageField的存储后端是可插入的。这是一个示例插件:`http://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
有示例代码,演示如何写入。所以我不会进入那个。
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"
您的instance.pk
将为无
如果这是对现有文件的修改,则将设置PK。
如果这是pre_save
答案 2 :(得分:0)
让我永远学习如何使用ImageField保存图像。事实证明这很容易 - 一旦你知道怎么做,它至少就是这样。我的意思是,在你看到之后,这一切都明显地结合在一起。
所以基本上,你正在使用FileField。我已经研究过ImageField和FileField之间的区别:
使用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%}
确保正确配置您的网址以处理图片或图片无法正常工作。将其添加到您的网址:
urlpatterns += patterns('',
url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
'document_root': settings.MEDIA_ROOT,
}),
)