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

时间:2017-02-15 22:41:00

标签: python django heroku django-staticfiles django-middleware

在Django中为生产提供图像文件的最佳做法是什么?我想用静态图像回复,并且我将我的Django应用程序部署到Heroku。

在效率或安全性方面使用django.middleware.security.SecurityMiddleware而不是whitenoise.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)

1 个答案:

答案 0 :(得分:9)

您的评论:

  

这里的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 docsWhitenoise docs

回复你的编辑:

在Django中,静态是指您在开发期间创建的JS / CSS /图像,在部署应用程序时不会更改(保持静态)。媒体是用户上传的图像和视频,或生成的图像(例如缩略图)。

Django建议在STATIC_ROOT和MEDIA_ROOT目录中单独存储静态和媒体。然后,在生产中,您通常会在这些目录中为URL STATIC_URL和MEDIA_URL配置Web服务器。 Whitenoise通过正确提供来自这些文件夹的文件简化了一些事情,而无需配置Web服务器。

让Django正确提供图像(静态或媒体)的主要问题如下:

  • 性能 - Django没有针对服务资产进行优化,因此它比通过Nginx / Apache等Web服务器提供服务要慢。大量的图像请求也会降低标准页面请求的速度,因为它们会排队并导致更长的响应时间。当您的网站很小时,这可能无关紧要,但是当您有网站流量时改变网站的工作方式很棘手!

  • 缓存标头 - Django不知道在返回图像时会向图像添加缓存控制标头,而像Whitenoise这样的软件包会添加合理的缓存标头(最重要的是缓存过期,例如用户的缓存时间)浏览器挂在你的图像上。)

根据您返回图像的方式,Whitenoise可能会处理Django可能没有的其他标题:

  • 媒体类型 - 浏览器需要知道如何处理他们所获得的响应,因此有一个名为Content-Type的标头。使用上面的代码,您将每个文件作为图像返回 - 如果用户要求PNG会怎么样?

  • 内容长度 - 浏览器使用内容长度(响应的大小)来显示进度条和其他优化(例如以块的形式读取响应)。

  • 压缩 - 大多数浏览器和Web服务器(以及Whitenoise)都支持压缩方法,例如gzip或最近的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服务器的另一个原因是可能会出现安全漏洞!

    我们假设视图中提供图片的代码如下,并且网址设置为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的存储服务)而不是文件系统上存储图像。您还可以将图像存储在数据库中,但这不能很好地扩展。有关设置为在S3上存储媒体的Django应用程序示例,请参阅https://github.com/eknuth/django-heroku-s3-bootstrap-demo