django:如何将传入的 POST 数据作为类似文件的 obj 进行流处理

Posted

技术标签:

【中文标题】django:如何将传入的 POST 数据作为类似文件的 obj 进行流处理【英文标题】:django: how to consume incoming POST data as a file-like obj for streaming processing 【发布时间】:2017-11-04 11:49:39 【问题描述】:

我正在使用 python + Django 处理传入的 Web 请求,这些请求可以将大量 JSON 附加为 POST 数据字段之一(例如 var1=abc&json_var=lots_of_data&other_var=xxx)。我想使用我自己的流 json 解析器以流方式处理 JSON,该解析器将类似文件的句柄作为其输入参数。从https://docs.djangoproject.com/en/1.11/ref/request-response/ 看来,这是可行的,使用HttpRequest.__iter__(),但我找不到任何如何用我自己的代码实现这一点的示例(即不仅仅是导入像 xml.etree.ElementTree 这样的库)。

基本上,我想做以下事情:

带有大 JSON 的 POST 请求 => Django/python => 创建类似文件的句柄以读取 POST => 流式 url 解码器 => 流式 JSON 处理器

我可以将 ijson 用于流式 JSON 处理器。如何填补为 POST 数据创建类似文件的句柄并将其传递给流式 url 解码器的两个空白?我不想自己动手,但我想如果有必要我可以。

【问题讨论】:

【参考方案1】:

__iter__() 只是 xreadlines 的包装器,而 xreadlines 又只是一个循环,它使用HttpRequest 的输入流一次调用并生成一行数据。因此,您可以将手册中的示例代码替换为这样的时间

parser = MyJsonParser()
for line in (request):
    parser.process(line)

您尚未发布代码,因此您必须根据自己的需要进行调整。

另外请注意,这可能是也可能不是真正的流式传输过程,具体取决于您的设置。您的服务器机制很可能会一次读取整个帖子正文并将其传递给您的视图,而无需一次发送一行。在这种情况下,流式传输的外观是虚幻的。

【讨论】:

谢谢。假设传入的请求以分块方式编码,有没有办法确保服务器也以块的形式摄取数据——或者至少,限制它分配的用于摄取发布数据的内存? IE。如果传入的 POST 大小为 2GB,我不希望服务器将其全部放入内存中。理想情况下,我可以在数据进入时对其进行处理,或者在处理数据之前使用固定内存将数据写入磁盘。 我认为该评论应该是一个新问题。即使作为一个问题,它也太宽泛了。您必须将其限制为特定服务器 此外,如果 POST 数据是 url 编码的,那么对迭代器的第一次调用将在一行中返回所有数据,即使它是例如2GB(或者如果发送分块可能不会?)重新“将其限制为特定服务器”:我认为问题隐含指定了一个服务器(Django的默认Web服务器) - 你能解释一下需要什么add'l信息? 另外,for line in (request): 在分块发送 POST 时没有产生任何结果。重新服务器详细信息,我正在使用cherrypy 进行测试,但我希望请求接口会将其抽象出来 你绝对不应该在这里使用 django 开发服务器!!!【参考方案2】:

我只能通过滚动自己的生成器和迭代器来解决这个问题。解决这个问题有几个关键:

查找在分块发送数据的情况下如何访问 POST 数据的类文件句柄。我可以在 request.META.get('wsgi.input') 找到这个,我通过使用 this post 转储所有请求属性找到它 滚动我自己的生成器来读取类似文件的句柄和 yield (varname, data_chunk) 对 滚动我自己的生成器,基于this post 的修改版本,创建一个类似文件的句柄,它具有正常的 read() 操作,但具有三个附加功能: f.varname 返回当前正在读取的变量的名称 数据在从 read() 传回之前是 url-unencoded f.next_pair() 使句柄前进以读取下一个变量。因此,调用 f.read() 直到它完成第一个变量,然后如果有另一个, f.next_pair() 将返回 true 并且可以再次调用 f.read() 直到完成读取下一个变量 可以在主循环中实现进一步的流处理

把它们放在一起看起来像:

f = request.META.get('wsgi.input')
ff = some_magic_adaptor(qs_from_file_to_generator(f))

while ff.next_pair():
    print 'varname:' + ff.varname
    if ff.varname == 'stream_parse_this':
        parser = stream_parser(ff)
        for event_results in parser:
            do_something

    while True:
        data = ff.read(buffer_size)
        if not data:
            break
        do_something_with_data_chunk(data)

【讨论】:

以上是关于django:如何将传入的 POST 数据作为类似文件的 obj 进行流处理的主要内容,如果未能解决你的问题,请参考以下文章

如何将 POST 数据中的 CSRF 令牌传递给 Django?

通过 Django 将表单 POST 到数据库不保存数据

如何将从表单提交中检索到的参数作为参数传递给 django 中的函数

如何使用无效标识符作为 django 表单字段名称

基于 Django 类的 post-only 视图

Django 视图层