在 Django 中为大型非本地文件流式传输 zip 可能吗?

Streaming zip in Django for large non-local files possible?

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

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

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

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

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

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

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

好吧,这很艰难!

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

  2. a) 你可以传送一个流,它可以在最后一个磁带存档又名 tar 文件,其中包括所有压缩文件。

-- 或--

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

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

这对我来说听起来像是一个完美的用例,需要解决队列作业并在后台处理它们。

优点:

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

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

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

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

总结一下:

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