在Django中提供静态文件的最佳实践

3

在Django生产环境中,提供图像文件的最佳实践是什么?我想用静态图片进行响应,并将我的Django应用部署到Heroku。

在效率或安全性方面,使用django.middleware.security.SecurityMiddlewarewhitenoise.middleware.WhiteNoiseMiddleware是否存在重大缺点?

与使用Whitenoise相比,下面的代码是否效率低下?从settings.MEDIA_ROOT提供图像与提供静态文件是否相同?

img =  os.path.join(settings.MEDIA_ROOT, filename)
try:
    with open(img, "rb") as f:
        return HttpResponse(f.read(), content_type="image/jpeg")
except IOError:
    failedResponse = '{"detail": "No image found"}'
    return HttpResponse(failedResponse)

你在添加Django中间件时遇到了哪些问题? - Cadmus
白噪声文档此处指出我们不应同时使用中间件,我只想知道是否使用白噪声替代django.security存在缺点。 - Manan Mehta
1
Django具有一些内置的安全机制,无论请求来自何处,它都会测试入侵者的行为是否存在恶意行为等。因此,我的建议是跟随Django中间件,因为我们可以信任它。但是,whitenoise是第三方包,我们不能在没有了解的情况下信任它。如果您遇到与静态文件相关的任何问题,我们可以进行纠正。 - Cadmus
我想将后端的Restful API和客户端的UI(使用AngularJS)构建在同一个服务器上,但据我所知,如果在settings.py中将debug设置为False,Django不会提供静态页面。虽然我可能是错的。您有什么建议应该如何做? - Manan Mehta
1
那就是问题所在...好的,我明白了。我可以把解决方案添加到答案部分吗? - Cadmus
2
@MananMehta,你能看一下我的回答吗?我不认为SnakeFcz的建议是一个好主意。 - Alex L
1个回答

10

您的评论:

这里的whitenoise文档说我们不应该同时使用中间件,我只想知道使用whitenoise是否存在缺点,相对于django.security——Manan Mehta 30分钟之前

不,它没有说那个-文档中的那一节是指MIDDLEWARE_CLASSES的顺序。你可以放心地将Whitenoise与Django的安全中间件一起使用。

下面是文档中的摘录:

编辑您的settings.py文件,并将WhiteNoise添加到MIDDLEWARE_CLASSES列表中,在除Django的SecurityMiddleware之外的所有其他中间件之上:

MIDDLEWARE_CLASSES = [  
    # 'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  
    # ...
]
SnakeFcz的强制让Django提供静态文件的建议不是一个好主意 - Whitenoise包是专门为通过Django提供静态文件设计的高性能方法。请参见 heroku文档Whitenoise文档
在Django中,静态文件是指像JS/CSS/images这样的你在开发期间创建的不会改变(保持静态)的东西。媒体则是像用户上传的图像和视频或生成的图像(例如缩略图)之类的东西。
Django建议将静态文件和媒体分别存储在STATIC_ROOT和MEDIA_ROOT目录中。然后,在生产中,您通常会在这些目录下配置Web服务器以获取URL STATIC_URL和MEDIA_URL。Whitenoise可以通过正确地从这些文件夹中提供文件来简化这些过程,而无需配置Web服务器。
使Django能够正确提供图像(静态或媒体)的主要问题如下:
- 性能 - Django未经过优化,因此比通过诸如Nginx / Apache之类的Web服务器提供服务慢。大量的图片请求也会减慢标准页面请求的速度,因为它们会排队并导致更长的响应时间。当您的网站很小时,这可能无关紧要,但在拥有流量时更改网站工作方式会很棘手!
- 缓存标头 - 当返回图像时,Django不知道添加缓存控制标头,而像Whitenoise这样的包添加了明智的缓存标头(最重要的是缓存到期时间,例如用户浏览器保留图像的时间)。
Whitenoise处理的其他一些标头可能取决于您返回图像的方式:
- 媒体类型 - 浏览器需要知道如何处理返回的响应,因此有一个名为Content-Type的标头。使用您上面的代码,您将每个文件都返回为图像 - 如果用户请求PNG呢?
- 内容长度 - 浏览器使用内容长度(响应大小)来显示进度条和其他优化(例如分块读取响应)。
- 压缩 - 大多数浏览器和Web服务器(以及Whitenoise)支持压缩方法,例如Gzip或最近由Google开发的Brotli。 Web服务器会压缩文件(通常只压缩一次,然后缓存压缩文件),以在传输期间最小化带宽。根据图像和格式,您可以将图像压缩到其大小的约60-70%左右。
演示 Lena 位图:
❯ brew install gzip brotli

❯ gzip -k -v lena.bmp
lena.bmp:      18.3% -- replaced with lena.bmp.gz

❯ bro --input lena.bmp --output lena.bmp.bro

❯ ls -lh lena*
-rw-r--r--@ 1 alex  staff   768K Feb 16 21:41 lena.bmp
-rw-------  1 alex  staff   527K Feb 16 21:45 lena.bmp.bro
-rw-r--r--@ 1 alex  staff   627K Feb 16 21:41 lena.bmp.gz
  • 安全性 - 将静态资产的服务留给Web服务器的另一个原因是潜在的安全漏洞!

    假设您视图中用于提供图像的代码如下,并且该URL设置为static/<filename>

  • img = os.path.join(settings.MEDIA_ROOT, filename)
    with open(img, "rb") as f:
        return HttpResponse(f.read(), content_type="image/jpeg")
    

    想象一下,如果一个恶意用户导航到 yoursite.com/static//Users/alex/.ssh/id_rsa。那么文件名变成 /Users/alex/.ssh/id_rsa

    filename = '/Users/alex/.ssh/id_rsa'
    os.path.join(settings.MEDIA_ROOT, filename)
    # '/Users/alex/.ssh/id_rsa'
    

    然后视图读取您的Web服务器私钥,并将其返回给恶意用户。哎呀!现在他们可以ssh进入您的服务器。

    Heroku上的媒体:

    如果您要部署到Heroku,请记住他们的dynos工作方式。Heroku dynos经常创建和销毁(每次部署时,至少每天一次),因此您无法依赖文件系统来保留数据。您也可以同时运行两个或多个dynos - 这些是完全独立的容器,在数据中心的不同主机上运行,它们不共享文件系统。通常,如果您想处理用户上传的媒体文件,则会使用Django-storages将图像存储在S3(AWS的存储服务)而不是文件系统上。您还可以将图像存储在数据库中,但这样做可能无法很好地扩展。请参见https://github.com/eknuth/django-heroku-s3-bootstrap-demo,了解一个设置为将媒体存储在S3上的示例Django应用程序。


    3
    不,任何方式都不是“正确”的 - Django并非为在生产中提供静态文件而设计,而Whitenoise则是。我认为你不应该在不了解用户尝试使用的软件包的情况下向初学者用户提供建议。 - Alex L
    大多数情况下,我不建议使用第三方包。 - Cadmus
    2
    @SnakeFcz,Heroku文档非常清晰 - “Django不支持在生产环境中提供静态文件。但是,出色的WhiteNoise项目可以集成到您的Django应用程序中,并且正是为此目的而设计的。” - Alex L
    4
    @SnakeFcz,原帖使用的是Heroku平台!问题中提到:“我想使用静态图片作为响应, 并将我的Django应用部署到Heroku上。” - Alex L
    1
    @MananMehta 我稍微扩展了我的回答,希望能有所帮助! - Alex L
    显示剩余3条评论

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接