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

Man*_*hta 3 python django heroku django-middleware django-staticfiles

在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)
Run Code Online (Sandbox Code Playgroud)

Ale*_*x L 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',  
    # ...
]
Run Code Online (Sandbox Code Playgroud)

SnakeFcz建议强制Django提供静态服务并不是一个好主意 - Whitenoise软件包被设计为一种通过Django提供静态服务的高性能方法.请参阅heroku文档和Whitenoise文档.


回复你的编辑:

在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
    
    Run Code Online (Sandbox Code Playgroud)
  • 安全性 - 将静态资产提供给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")
    
    Run Code Online (Sandbox Code Playgroud)

    想象一下,如果恶意用户导航到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'
    
    Run Code Online (Sandbox Code Playgroud)

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


Heroku上的媒体:

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

  • @SnakeFcz操作系统正在使用Heroku!从Q:"我想用静态图像回复,我正在将我的django应用程序部署到Heroku." (4认同)
  • @SnakeFcz不,任何方式都不是"正确" - Django不是为生产中的静态文件服务而且Whitenoise*是*.我不认为你应该在不了解他们试图使用的软件包的情况下向初学者用户提出建议. (3认同)
  • @SnakeFcz [Heroku文档](https://devcenter.heroku.com/articles/django-assets#whitenoise)非常清楚 - "Django不支持在生产中提供静态文件.但是,梦幻般的WhiteNoise项目可以集成到你的Django应用程序,设计时考虑到了这个目的." (2认同)