bigywater.tech
Flask · 177

媒体文件生产部署

用户上传的文件该由谁来发?公开与私有的媒体怎么区分?怎么既做权限检查又不占 Python worker?这篇用真实的 nginx 把两种方式的响应逐项验证(上传校验见第 126 篇,大文件见第 169 篇)。

X-Accel-Redirect
Flask 通过它把「发文件」交给 nginx,权限检查留在应用里
404
外部直接访问 internal location 的结果
sandbox
公开媒体目录上加的 CSP 指令,限制 SVG 等文件执行脚本

公开媒体

实测 · 本机执行输出
  GET /media/a.png → 200 | Content-Type image/png | X-Content-Type-Options nosniff | CSP default-src 'none'; sandbox | Cache-Control max-age=604800
  GET /media/v.svg → 200 | Content-Type image/svg+xml(SVG 可以内嵌脚本,所以加了 CSP sandbox 限制它)
  GET /media/nope.png                → 404
  GET /media/                        → 403
  GET /media/../private/secret.txt   → 404

上一篇:静态文件生产部署;下一篇:Docker 部署 Flask。

nginx 是自己起的实例(非默认端口)。

私有媒体

实测 · 本机执行输出
  未登录访问 /docs/1                      → 401 | b'<!doctype html>\n<html lang=en>\n<title>40' | Content-Disposition: None
  ann 访问自己的 /docs/1                  → 200 | b'%PDF-1.4 ann-report' | Content-Disposition: attachment; filename*=UTF-8''%E5%B9%B4%E5%BA%A6%20%E6%8A%A5%E5%91%8A.pdf
  ann 访问 bob 的 /docs/2               → 403 | b'<!doctype html>\n<html lang=en>\n<title>40' | Content-Disposition: None
  bob 访问自己的 /docs/2                  → 200 | b'bob secret' | Content-Disposition: attachment; filename*=UTF-8''secret.txt
  访问不存在的 /docs/9                     → 404 | b'<!doctype html>\n<html lang=en>\n<title>40' | Content-Disposition: None
  直接请求内部地址 /_protected/report.pdf → 404(internal location 只能由 nginx 内部重定向访问)
  带 Range 的授权请求 → 206 | b'%PDF-' | Content-Range bytes 0-4/19(nginx 对 X-Accel-Redirect 的文件也支持 Range)

分步演示

媒体文件生产部署
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。nginx 1.31.6(自己的配置、非默认端口)、gunicorn
    • 没有验证:对象存储与预签名 URL、SVG 在真实浏览器里的脚本执行与 CSP 的实际拦截、文件权限与属主、多台机器共享媒体目录(NFS)、图片缩略图的动态生成与缓存
    京ICP备2026067502号-1京公网安备11010502063487号