从 Django 服务器一次传输多个文件

2024-03-14

我正在运行 Django 服务器来为受保护网络中的另一台服务器提供文件。当用户请求一次访问多个文件时,我希望 Django 服务器将这些文件一次性传输给该用户。

由于在浏览器中一次下载多个文件并不容易,因此需要以某种方式捆绑文件。我不希望我的服务器必须先下载所有文件,然后提供现成的捆绑文件,因为这会增加较大文件的大量时间损失。对于 zips,我的理解是它在组装时无法进行流式传输。

有没有什么方法可以在远程服务器的第一个字节可用时立即开始流式传输容器?


Tar 文件用于将多个文件收集到一个存档中。它们是为磁带录音机开发的,因此提供顺序写入和读取。

使用 Django 可以将文件流式传输到浏览器FileResponse() https://docs.djangoproject.com/en/3.1/ref/request-response/#fileresponse-objects,它可以使用生成器作为参数。

如果我们为它提供一个生成器,将 tar 文件与用户请求的数据组合在一起,那么 tar 文件就会及时生成。然而 python 内置tarfile https://docs.python.org/3/library/tarfile.html-module 不提供这种开箱即用的功能。

然而我们可以利用tarfile能够传入类似文件的对象来自己处理存档的组装。因此我们可以创建一个BytesIO() https://docs.python.org/3/library/io.htmltarfile 将逐步写入并将其内容刷新到 Django 的对象FileResponse()方法。为此,我们需要实现一些方法FileResponse() and tarfile期望访问。让我们创建一个类FileStream:

class FileStream:
    def __init__(self):
        self.buffer = BytesIO()
        self.offset = 0

    def write(self, s):
        self.buffer.write(s)
        self.offset += len(s)

    def tell(self):
        return self.offset

    def close(self):
        self.buffer.close()

    def pop(self):
        s = self.buffer.getvalue()
        self.buffer.close()
        self.buffer = BytesIO()
        return s

现在当我们write()数据到FileStream的缓冲区和yield FileStream.pop()Django 会立即将该数据发送给用户。

作为数据,我们现在想要组装该 tar 文件。在里面FileStream类中我们添加另一个方法:

    @classmethod
    def yield_tar(cls, file_data_iterable):
        stream = FileStream()
        tar = tarfile.TarFile.open(mode='w|', fileobj=stream, bufsize=tarfile.BLOCKSIZE)

这创建了一个FileStream-内存中的实例和文件句柄。文件句柄访问FileStream- 读取和写入数据的实例,而不是磁盘上的文件。

现在在 tar 文件中我们首先必须添加tarfile.TarInfo()表示顺序写入数据的标头的对象,其中包含文件名、大小和修改时间等信息。

        for file_name, file_size, file_date, file_data in file_data_iterable:
            tar_info = tarfile.TarInfo(file_name)
            tar_info.size = int(file_size)
            tar_info.mtime = file_date
            tar.addfile(tar_info)
            yield stream.pop()

您还可以查看将任何数据传递给该方法的结构。 file_data_iterable 是包含以下内容的元组列表
((str) file_name, (int/str) file_size, (str) unix_timestamp, (bytes) file_data).

发送 TarInfo 后,迭代 file_data。该数据需要是可迭代的。例如你可以使用requests.response您检索的对象requests.get(url, stream=True).

            for chunk in (requests.get(url, stream=True).iter_content(chunk_size=cls.RECORDSIZE)):
                # you can freely choose that chunk size, but this gives me good performance
                tar.fileobj.write(chunk)
                yield stream.pop()

注意:这里我使用了变量url请求文件。你需要通过它而不是file_data在元组参数内。如果您选择传递可迭代文件,则需要更新此行。

最后,tar 文件需要特殊的格式来指示文件已完成。 Tarfile 由块和记录组成。通常一个块包含512字节,一条记录包含20个块(20*512字节=10240字节)。首先,包含最后一个文件数据块的最后一个块用 NUL(通常是纯零)填充,然后下一个文件的下一个 TarInfo 标头开始。

要结束存档,当前记录将被 NUL 填充,但必须至少有两个块完全被 NUL 填充。这将由tar.close()。另请参阅此Wiki https://en.wikipedia.org/wiki/Tar_(computing)#File_format.

            blocks, remainder = divmod(tar_info.size, tarfile.BLOCKSIZE)
            if remainder > 0:
                tar.fileobj.write(tarfile.NUL * (tarfile.BLOCKSIZE - remainder))
                yield stream.pop()
                blocks += 1
            tar.offset += blocks * tarfile.BLOCKSIZE
        tar.close()
        yield stream.pop()

您现在可以使用FileStreamDjango 视图中的类:

from django.http import FileResponse
import FileStream

def stream_files(request, files):
    file_data_iterable = [(
        file.name,
        file.size,
        file.date.timestamp(),
        file.data
    ) for file in files]

    response = FileReponse(
        FileStream.yield_tar(file_data_iterable),
        content_type="application/x-tar"
    )
    response["Content-Disposition"] = 'attachment; filename="streamed.tar"'
    return response

如果您想传递 tar 文件的大小以便用户可以看到进度条,您可以提前确定未压缩的 tar 文件的大小。在里面FileStream类添加另一个方法:

    def tarsize(cls, sizes):
        # Each file is preceeded with a 512 byte long header
        header_size = 512
        # Each file will be appended to fill up a block
        tar_sizes = [ceil((header_size + size) / tarfile.BLOCKSIZE)
                     * tarfile.BLOCKSIZE for size in sizes]
        # the end of the archive is marked by at least two consecutive
        # zero filled blocks, and the final record block is filled up with
        # zeros.
        sum_size = sum(tar_sizes)
        remainder = cls.RECORDSIZE - (sum_size % cls.RECORDSIZE)
        if remainder < 2 * tarfile.BLOCKSIZE:
            sum_size += cls.RECORDSIZE
        total_size = sum_size + remainder
        assert total_size % cls.RECORDSIZE == 0
        return total_size

并使用它来设置响应标头:

tar_size = FileStream.tarsize([file.size for file in files])
...
response["Content-Length"] = tar_size

非常感谢chipx86 https://gist.github.com/chipx86/9598b1e4a9a1a7831054 and allista https://gist.github.com/allista/d3dc1984212d3d033eb29c78914e5887他们的要点对我完成这项任务有很大帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 Django 服务器一次传输多个文件 的相关文章

  • 如何有条件地组合两个相同形状的 numpy 数组

    这听起来很简单 但我想我把它想得太复杂了 我想创建一个数组 其元素是从两个形状相同的源数组生成的 具体取决于源数组中哪个元素更大 为了显示 import numpy as np array1 np array 2 3 0 array2 np
  • 如何用spaCy获取依赖树?

    我一直在尝试寻找如何使用 spaCy 获取依赖树 但我找不到任何有关如何获取树的信息 只能在如何导航树 https spacy io usage examples subtrees 如果有人想轻松查看 spacy 生成的依赖关系树 一种解决
  • 对打开文件的脚本进行单元测试

    我编写了一个脚本 它打开一个文件 读取内容并进行一些操作和计算 并将它们存储在集合和字典中 我该如何为这样的事情编写单元测试 我的问题具体是 我会测试文件是否打开 文件很大 这是unix字典文件 我如何对计算进行单元测试 我真的必须手动计算
  • 如何在“python setup.py test”中运行 py.test 和 linter

    我有一个项目setup py文件 我用pytest作为测试框架 我还在我的代码上运行各种 linter pep8 pylint pydocstyle pyflakes ETC 我用tox在多个 Python 版本中运行它们 并使用以下命令构
  • 远程控制或脚本打开 Office 从 Python 编辑 Word 文档

    我想 最好在 Windows 上 在特定文档上启动 Open Office 搜索固定字符串并将其替换为我的程序选择的另一个字符串 我该如何从外部 Python 程序中做到这一点 OLE 什么 原生 Python 脚本解决方案 The doc
  • pandas 两个数据框交叉连接[重复]

    这个问题在这里已经有答案了 我找不到有关交叉联接的任何内容 包括合并 联接或其他一些内容 我需要使用 my function 作为 myfunc 处理两个数据帧 相当于 for itemA in df1 iterrows for itemB
  • 一起使用 Argparse 和 Json

    我是 Python 初学者 我想知道 Argparse 和 JSON 是否可以一起使用 说 我有变量p q r 我可以将它们添加到 argparse 中 parser add argument p param1 help x variabl
  • 使用reduce方法的斐波那契数列

    于是 我看到有人用reduce方法来计算斐波那契数列 这是他的想法 1 0 1 1 2 1 3 2 5 3 对应于 1 1 2 3 5 8 13 21 代码如下所示 def fib reduce n initial 1 0 dummy ra
  • 以编程方式将列名称添加到 numpy ndarray

    我正在尝试将列名称添加到 numpy ndarray 然后按名称选择列 但这不起作用 我无法判断问题是在添加名称时出现 还是在稍后尝试调用它们时出现 这是我的代码 data np genfromtxt csv file delimiter
  • 在 Windows 上将 Word2vec 与 Tensorflow 结合使用

    In 本教程文件 https github com tensorflow models blob master tutorials embedding word2vec py L45通过 Tensorflow 找到以下行 第 45 行 来加
  • Python 相当于 Bit Twiddling Hacks 中的 C 代码?

    我有一个位计数方法 我正在尝试尽可能快地实现 我想尝试下面的算法位摆弄黑客 http graphics stanford edu seander bithacks html CountBitsSetParallel 但我不知道 C 什么是
  • 如何获取 Matplotlib 生成的散点图的像素坐标?

    我使用 Matplotlib 生成散点图的 PNG 文件 现在 对于每个散点图 除了 PNG 文件之外 我还会also就像生成散点图中各个点的像素坐标列表一样 我用来生成散点图 PNG 文件的代码基本上是这样的 from matplotli
  • 如何将reportlab与Google应用程序引擎一起使用

    我无法在谷歌应用程序引擎下正确导入reportlab 根据以下guide http blog notdot net 2010 04 Generating PDFs on App Engine Python and introducing M
  • Python:如何从文件中的一行读取字符并将它们转换为浮点数和字符串,具体取决于它们是数字还是字母?

    我有一个如下所示的文件 1 1 C C 1 9873 2 347 3 88776 1 2 C Si 4 887 9 009 1 21 我想逐行读取文件的内容 当我使用的行上只有数字时 for line in readlines file d
  • Scikit Learn - K-Means - 肘部 - 标准

    今天我想学习一些关于 K means 的知识 我已经了解该算法并且知道它是如何工作的 现在我正在寻找正确的 k 我发现肘部准则作为检测正确的 k 的方法 但我不明白如何将它与 scikit learn 一起使用 在 scikit learn
  • 如何表示类的实例与将其作为输入的类之间的关系?

    我有一堂课叫House 这个类的实例是house class House def init self height length self height height self length length def housePlan hou
  • python csv按列转换为字典

    是否可以将 csv 文件中的数据读取到字典中 使得列的第一行是键 同一列的其余行构成列表的值 例如 我有一个 csv 文件 strings numbers colors string1 1 blue string2 2 red string
  • DRF:以编程方式从 TextChoices 字段获取默认选择

    我们的网站是 Vue 前端 DRF 后端 在一个serializer validate 方法 我需要以编程方式确定哪个选项TextChoices类已被指定为模型字段的默认值 TextChoices 类 缩写示例 class PaymentM
  • Windows 与 Linux 文本文件读取

    问题是 我最近从 Windows 切换到 Ubuntu 我的一些用于分析数据文件的 python 脚本给了我错误 我不确定如何正确解决 我当前仪器的数据文件输出如下 Header 有关仪器等的各种信息 Data 状态 代码 温度 字段等 0
  • 从 Flask 中的 S3 返回 PDF

    我正在尝试在 Flask 应用程序的浏览器中返回 PDF 我使用 AWS S3 来存储文件 并使用 boto3 作为与 S3 交互的 SDK 到目前为止我的代码是 s3 boto3 resource s3 aws access key id

随机推荐

  • 带有错误模块“main:Main”的 stack ghci 在多个文件中定义:

    我有一个小的 haskell 程序 它可以使用堆栈构建和执行 当我开始时stack ghci我收到一条错误消息 我不明白并且无法继续 GHCi version 8 10 4 https www haskell org ghc for hel
  • 来自本地的 IAM SAML 联合失败

    我在本地虚拟机中设置了 openldap 和 shibboleth idp 并在 aws 中创建了身份提供商并上传了元数据 在元数据中 url 指向我的本地 IP 地址 例如 SingleSignOnService Binding urn
  • 推导 pytorch 网络的结构

    对于我的用例 我需要能够采用 pytorch 模块并解释模块中的层序列 以便我可以以某种文件格式在层之间创建 连接 现在假设我有一个简单的模块 如下所示 class mymodel nn Module def init self input
  • 无法解析符号 DrawerLayout

    我正在尝试实现导航抽屉 如下所示 http developer android com training implementing navigation nav drawer html top http developer android
  • 在 HTML 链接中使用 onClick 传递多个值

    您好 我正在尝试使用 HTML onclick 函数传递多个值 我使用 Javascript 创建表 var user element UserName var valuationId element ValuationId Valuati
  • 无法解析 com.facebook.react:react-native:0.32.0

    我在 android studio 的 React Native 项目中打开了 android 文件夹 做了所有可能的修改build gradle文件 但是我收到这个重复的错误 Error Failed to resolve com fac
  • jQuery Mobile 绑定事件

    我在使用 jquery mobile 时遇到了一些问题 我的页面总是被调用这个函数运行 document bind pagechange function peforms ajax operations 问题是 每次查看我的页面时 都会增加
  • Git 命令仅重置索引和工作树而不是 HEAD

    这个问题是后续问题这个问题 https stackoverflow com questions 44513186 how do i edit a commit with interactive rebase as uncommited 它试
  • C 中 If-Else 和三元运算符之间的速度差异...?

    因此 在同事的建议下 我刚刚测试了三元运算符和等效的 If Else 块之间的速度差异 并且三元运算符生成的代码似乎比 If Else 快 1 到 2 倍 我的代码是 gettimeofday tv3 0 for i 0 i lt N i
  • 在 R 中建立复杂方程模型

    我有以下模型 我在 R 中将其编码为 function t C Ao s wd ph C Ao exp s t cos wd t ph 我想用这个方程来形成一个预测模型 但是 我不知道如何成功运行或绘制这个方程 I tried nls但遇到
  • phpmyadmin 中的 $GLOBALS['cfg'] 在哪里

    我在phpmyadmin下的phpinfo php文件中发现 有这样的配置设置 if GLOBALS cfg ShowPhpInfo phpinfo 我想知道在哪里可以设置 GLOBALS cfg ShowPhpInfo 配置 当然 我可以
  • angularjs 编译 ng-controller 和插值

    On the docs https docs angularjs org api ng function angular injector我看到了一个后来添加的编译 东西 的例子 var div div content label div
  • Angularjs 会话存储和范围

    有人知道为什么我的范围变量没有更新吗 这让我难以置信 sessionStorage 变量很好 但是当我在范围变量中定义它们时 我收到 未定义 错误 我已经修改了 scope apply 但显然范围已经被消化了 S请帮助新手 if sessi
  • Javascript 从变量渲染 jpeg 二进制数据

    如何渲染 Javascript 变量中包含的 JPEG PNG 文件数据 是否可以 需要什么 JavaScript 库 使用的浏览器是FF IE Thanks 现代浏览器支持内联图像 http dean edwards name weblo
  • Django/Heroku:致命:角色的连接太多

    所以我刚刚通过 Heroku 业余爱好 和 Postgres 试用版 推出了一个包含 Channels 2 0 Daphne 2 2 0 和 asgi 的网站 当我启动网站时 我点击了几个页面 然后收到 500 错误 我通过电子邮件收到的错
  • 无法在 Outlook 中正确显示 html 电子邮件签名

    我用 html 为自己创建了一个简单的电子邮件签名 他的代码是 div style width 50px height 50px margin right 10px img src logo png div div style height
  • C# 读取Excel工作表

    有人可以帮我在 C 应用程序中阅读简单的 Excel 工作表吗 我希望能够迭代每一行并在每一列上有一个句柄 谢谢 杆 这是我发现的最简单的方法 从 C 创建 Excel XLS 和 XLSX 文件 https stackoverflow c
  • 如何使用域名setCookie和getCookie

    当我保存 cookie 时 它 会与当前 URL 链接一起存储 当我获取仅搜索当前 URL 的 cookie 时也是如此 我需要使用我自己定义的 URL 链接保存和检索 cookie 你能帮助我吗 您无法读取不同域设置的cookie 如果可
  • 有人能在 android studio 中使用 chromecast android 示例吗?

    我在尝试让这些文件正确加载时经历了一段糟糕的时光 要么是支持框架失败 要么是转换 API 失败 有人有可重复的方法吗 github 示例显示 即将推出 用于加载到 android studio 中 我还没有转换其中一个测试项目 但我已经能够
  • 从 Django 服务器一次传输多个文件

    我正在运行 Django 服务器来为受保护网络中的另一台服务器提供文件 当用户请求一次访问多个文件时 我希望 Django 服务器将这些文件一次性传输给该用户 由于在浏览器中一次下载多个文件并不容易 因此需要以某种方式捆绑文件 我不希望我的