【问题标题】:Streaming zip in Django for large non-local files possible?可以在 Django 中为大型非本地文件流式传输 zip?
【发布时间】:2021-03-18 21:21:42
【问题描述】:

我有一个用 Django 编写的代理,它接收对某些文件的请求。在决定是否允许用户查看文件后,代理从远程服务获取文件并将其提供给用户。它还有更多内容,但这是要点。

此设置适用于单个文件,但有一个新要求是用户希望将多个文件以 zip 格式一起下载。这些文件有时很小,但也可能变得非常大(100MB 以上),它可以同时包含 2 到 1000 个文件。这可能会变得非常大,并且首先获取所有这些文件,压缩它们然后在同一个请求中提供它们是一种负担。

我了解到创建“流式 zip”的可能性;一种打开 zip 然后开始发送该 zip 中的文件直到关闭它的方法。我找到了几个 php 示例,在 Python 中找到了 django-zip-stream extension。它们都假设本地存储的文件,django 扩展也假设使用 nginx。

在我的情况下,我想知道几件事:

  1. 我没有本地存储文件。我可以使用 async/await 结构来获取它们并同时为它们提供服务。这意味着我的内存中总是有两个文件(我当前正在服务的一个,以及我从源服务器获取的下一个)。
  2. 很遗憾,我无法控制提供此服务的 Web 服务器。我当然可以在它前面放一个 nginx 容器,但是我不认为 nginx 可以从我存储在 Python vars 中的文件中提供服务,因为我是从源服务器获取它们的。
  3. 无论我是在 Python 中执行此操作还是让它在 nginx 中压缩,我认为所需的 CPU 周期将是大量的。

有人知道在我设置非常大的远程文件时使用流式压缩文件是否是个好主意吗?我有点担心由于 CPU 或内存限制,许多请求很容易 DOS 我们的服务器。

我还可以构建一个队列来压缩文件并向用户发送电子邮件,但如果可能的话,我希望应用程序尽可能保持无状态。

【问题讨论】:

    标签: python django zip streaming


    【解决方案1】:

    好吧,这很难!

    1. 在第一个请求之后,您可以在文件服务器上创建并保存压缩文件。所以文件服务器总是在最后交付压缩文件。由于创建 zip 文件,第一次请求将花费更长的时间,但下一次它将始终交付压缩文件,只要它不会被删除。

    2. a) 您可以交付单个流,该流最后可能是磁带存档(也称为 tar 文件),其中包含所有压缩文件。

    -- 或--

    1. b) HTTP/2 “该协议的主要优点之一是它是多路复用的,这意味着可以在单个连接上传输多个文件。” (sitepoint.com) 浏览器应该没有任何问题 (caniuse.com)

    如果发生 DOS 攻击,您可以限制文件下载请求的数量。因此,如果同时有太多请求,它们将被退回,他们必须稍后再试。

    【讨论】:

      【解决方案2】:

      在我看来,这听起来像是一个完美的用例,可以解决排队作业并在后台处理它们。

      优点:

      1. 由于检索和压缩文件需要可变(并且可能很长)时间,因此应该将其与 HTTP 请求/响应周期分离;
      2. 多个作业将被序列化以在任务队列中执行。

      第二个优势尤其可取,因为您准备好接收多个并发请求。

      我还会考虑使用带有 FileField 的“任务”Django 模型用作生成的 zip 文件的容器,因此 Nginx 将从媒体文件夹中静态有效地提供它。 作为额外的好处,您将直接从 Django 管理用户界面监控正在发生的事情。

      我在许多 Django 项目中都使用过类似的方法,并且已被证明非常健壮且易于管理;您可能想快速浏览一下我正在使用的以下 django 应用程序:https://github.com/morlandi/django-task

      总结一下:

      • 编写一个带有 FileField 的“任务”模型,用作压缩结果的容器
      • 收到请求后,在“任务”表中插入新记录,在后台队列中插入新作业
      • 后台作业负责收集资源并压缩资源;这是常见的 Python 东西
      • 完成后,将结果保存在 FileField 中并向用户发送通知
      • 用户将按照收到的 url 将 zip 文件下载为静态文件

      【讨论】: