【问题标题】:Best practice to serve static files in Django在 Django 中提供静态文件的最佳实践
【发布时间】:2017-07-04 19:51:30
【问题描述】:

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

在效率或安全性方面,使用django.middleware.security.SecurityMiddleware 代替whitenoise.middleware.WhiteNoiseMiddleware 有什么明显的缺点吗?

与使用白噪声相比,下面的代码效率低吗?提供来自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 middileware 时遇到了什么问题
  • whitenoise 文档 here 说我们不应该同时使用这两个中间件,我只是想知道在 django.security 上使用 whitenoise 是否有缺点
  • Django 有一些内置的安全机制,无论向我们的系统发出什么请求,它都会测试入侵者的行为,是否有任何恶意软件行为等,所以我的建议是跟进我们可以信任的 Django 中间件。但是白噪声是第三方软件包,除非不知情,否则我们无法信任它们。如果您遇到任何与静态文件相关的问题,我们可以纠正
  • 这就是问题所在……好的。我可以在答案部分添加解决方案吗?
  • @MananMehta 你能看看我的回答吗?我不认为 SnakeFcz 的建议是个好主意。

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


【解决方案1】:

您的评论:

这里的 whitenoise 文档说我们不应该同时使用这两个中间件,我只是想知道在 django.security 上使用 whitenoise 是否有缺点 – 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 服务器提供服务要慢。许多图像请求也会减慢标准页面请求,因为它们会排队并导致更长的响应时间。当您的网站很小时,这可能无关紧要,但是当您有流量时改变网站的工作方式很棘手!

  • 1234563浏览器挂在您的图像上)。

还有一些 Whitenoise 可能无法处理的标头,具体取决于您返回图像的方式:

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

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

  • 压缩 - 大多数浏览器和网络服务器(和 Whitenoise)都支持压缩方法,例如 gzip 或最近的 brotli(由 google 构建)。 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'
    

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


Heroku 上的媒体:

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

【讨论】:

  • 编码我们无论如何都可以做对,我尝试使用Django内置的中间件,我认为他们可能没有性能问题
  • @SnakeFcz 不,任何方式都不是“正确的” - Django 并非设计用于在生产中提供静态文件,而 Whitenoise 。我认为您不应该在不了解他们尝试使用的软件包的情况下向初学者提出建议。
  • @SnakeFcz Heroku documentation 非常清楚——“Django 不支持在生产环境中提供静态文件。但是,出色的 WhiteNoise 项目可以集成到您的 Django 应用程序中,并且正是为了这个目的而设计的介意。”
  • @SnakeFcz 该操作正在使用 Heroku!来自问:“我想用静态图像进行响应,我正在将我的 django 应用程序部署到 Heroku。”
  • @MananMehta 我已经扩展了我的答案,希望对您有所帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多