在 Django 中提供静态文件的最佳实践
Best practice to serve static files in Django
在 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)
您的评论:
The whitenoise documentation here says that we should not use both the middlewares and I just wanted to know if there was a drawback of using whitenoise over django.security – Manan Mehta 30 mins ago
不,它没有这么说 - 文档的那部分指的是 MIDDLEWARE_CLASSES 的顺序。配合Django的安全中间件就可以愉快的使用Whitenoise了。
摘自以下文档:
Edit your settings.py file and add WhiteNoise to the MIDDLEWARE_CLASSES list, above all other middleware apart from Django’s SecurityMiddleware:
MIDDLEWARE_CLASSES = [
# 'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
SnakeFcz 强制 Django 提供静态服务的建议不是一个好主意 - Whitenoise 包被设计为通过 Django 提供静态服务的高性能方法。参见heroku docs and the Whitenoise docs。
回复您的编辑:
在 Django 中,静态指的是您在开发过程中创建的类似 JS/CSS/images 的内容,这些内容在部署应用程序时不会更改(保持 static)。媒体是用户上传的图像和视频,或生成的图像(例如缩略图)。
Django 建议将静态和媒体分开存储在 STATIC_ROOT 和 MEDIA_ROOT 目录中。然后在生产中,您通常会在这些目录中为 URL STATIC_URL 和 MEDIA_URL 配置 Web 服务器。 Whitenoise 通过正确地提供这些文件夹中的文件来简化事情,而无需配置 Web 服务器。
让 Django 正确提供图像(静态或媒体)的主要问题如下:
性能 - Django 未针对服务资产进行优化,因此它会比通过 Nginx/Apache 等网络服务器提供服务慢。大量图像请求也会减慢标准页面请求,因为它们会 queue 并导致更长的响应时间。当您的网站较小时,这可能无关紧要,但当您有流量时,改变网站的工作方式就很棘手了!
缓存 headers - Django 不知道在 returned 时将 cache-control headers 添加到图像,而像 Whitenoise 这样的软件包添加了合理的缓存 headers(最重要的是缓存过期,例如用户的浏览器在您的图像上停留多长时间)。
还有一些其他的 header Whitenoise 可以处理 Django 可能不会处理的问题,这取决于您 return 图片的方式:
媒体类型 - 浏览器需要知道如何处理他们收到的响应,所以有一个 header 叫做 Content-Type
。使用上面的代码,您 return 将每个文件都作为图像 - 如果用户要求 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'
然后视图读取您的网络服务器的私钥,并return它给恶意用户。哎呀!现在他们可以通过 ssh 进入您的服务器。
Heroku 上的媒体:
如果您要部署到 Heroku,要记住的一件事是他们的 dynos 的工作方式。 Heroku dynos 的创建和销毁非常频繁(每次部署时,至少每天),因此您不能依赖文件系统来维持运行。您还可以同时 运行 两个或多个测功机 - 这些是完全独立的容器 运行 在数据中心的不同主机上,它们不共享文件系统。通常,如果您想处理用户上传的媒体,您将使用 Django-storages to store images on S3 (AWS's storage service) instead of the filesystem. You could also store images in your database, but that doesn't scale as nicely. See https://github.com/eknuth/django-heroku-s3-bootstrap-demo 作为示例 Django 应用程序,设置为在 S3 上存储媒体。
在 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)
您的评论:
The whitenoise documentation here says that we should not use both the middlewares and I just wanted to know if there was a drawback of using whitenoise over django.security – Manan Mehta 30 mins ago
不,它没有这么说 - 文档的那部分指的是 MIDDLEWARE_CLASSES 的顺序。配合Django的安全中间件就可以愉快的使用Whitenoise了。
摘自以下文档:
Edit your settings.py file and add WhiteNoise to the MIDDLEWARE_CLASSES list, above all other middleware apart from Django’s SecurityMiddleware:
MIDDLEWARE_CLASSES = [
# 'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
SnakeFcz 强制 Django 提供静态服务的建议不是一个好主意 - Whitenoise 包被设计为通过 Django 提供静态服务的高性能方法。参见heroku docs and the Whitenoise docs。
回复您的编辑:
在 Django 中,静态指的是您在开发过程中创建的类似 JS/CSS/images 的内容,这些内容在部署应用程序时不会更改(保持 static)。媒体是用户上传的图像和视频,或生成的图像(例如缩略图)。
Django 建议将静态和媒体分开存储在 STATIC_ROOT 和 MEDIA_ROOT 目录中。然后在生产中,您通常会在这些目录中为 URL STATIC_URL 和 MEDIA_URL 配置 Web 服务器。 Whitenoise 通过正确地提供这些文件夹中的文件来简化事情,而无需配置 Web 服务器。
让 Django 正确提供图像(静态或媒体)的主要问题如下:
性能 - Django 未针对服务资产进行优化,因此它会比通过 Nginx/Apache 等网络服务器提供服务慢。大量图像请求也会减慢标准页面请求,因为它们会 queue 并导致更长的响应时间。当您的网站较小时,这可能无关紧要,但当您有流量时,改变网站的工作方式就很棘手了!
缓存 headers - Django 不知道在 returned 时将 cache-control headers 添加到图像,而像 Whitenoise 这样的软件包添加了合理的缓存 headers(最重要的是缓存过期,例如用户的浏览器在您的图像上停留多长时间)。
还有一些其他的 header Whitenoise 可以处理 Django 可能不会处理的问题,这取决于您 return 图片的方式:
媒体类型 - 浏览器需要知道如何处理他们收到的响应,所以有一个 header 叫做
Content-Type
。使用上面的代码,您 return 将每个文件都作为图像 - 如果用户要求 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'
然后视图读取您的网络服务器的私钥,并return它给恶意用户。哎呀!现在他们可以通过 ssh 进入您的服务器。
Heroku 上的媒体:
如果您要部署到 Heroku,要记住的一件事是他们的 dynos 的工作方式。 Heroku dynos 的创建和销毁非常频繁(每次部署时,至少每天),因此您不能依赖文件系统来维持运行。您还可以同时 运行 两个或多个测功机 - 这些是完全独立的容器 运行 在数据中心的不同主机上,它们不共享文件系统。通常,如果您想处理用户上传的媒体,您将使用 Django-storages to store images on S3 (AWS's storage service) instead of the filesystem. You could also store images in your database, but that doesn't scale as nicely. See https://github.com/eknuth/django-heroku-s3-bootstrap-demo 作为示例 Django 应用程序,设置为在 S3 上存储媒体。