【问题标题】:requests - how to stream upload - partial file请求 - 如何流式上传 - 部分文件
【发布时间】:2015-06-28 19:02:48
【问题描述】:

我的目标是使用请求对文件的一部分进行 PUT 并流式传输文件(即,不将其加载到内存中然后执行 PUT)。

This page 解释了如何对整个文件执行此操作:

Requests 支持流式上传,允许您发送大量 流或文件而不将它们读入内存。流式传输和 上传,只需为您的 body 提供一个类似文件的对象:

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

但是,就我而言,我只想发送文件的一部分。有没有办法做到这一点?

在概念上,类似于:

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f.read(chunksize))

【问题讨论】:

  • 嗯,您可能会编写一个生成器,假装是一个类似文件的对象,它将在幕后读取一个块,但可能会很棘手,因为我不确定请求对文件执行什么调用,但如果没有更好的解决方案似乎是可能的
  • @user3012759:我尝试在类似文件的对象中搜索 Request 所需的内容,但无济于事。但是,请注意,它接受 Chunk-Encoded Requests 的简单生成器
  • 我想您可以尝试使用具有readclose 方法的基本类文件类,如果这不起作用,请继续向您的类添加方法,直到请求停止抱怨。 :)
  • 我不清楚“我的目标是使用请求对文件的一部分进行 PUT 并流式传输文件”是什么意思。您是否也不想将该 chunk 读入内存?如果这就是你想要的,我可以帮你想出一个解决方案。为了您的信息,如果将块读入内存是可以的,那么您的第二个 sn-p 就可以正常工作。
  • @sigmavirus24,没错,我不想将所有的 'f.read(chunksize)' 加载到内存中。

标签: python python-requests


【解决方案1】:

根据 Greg 对我的问题的回答,我认为以下方法效果最好:

首先,您需要一些东西来包装打开的文件,以限制可以读取的数据量:

class FileLimiter(object):
    def __init__(self, file_obj, read_limit):
        self.read_limit = read_limit
        self.amount_seen = 0
        self.file_obj = file_obj

        # So that requests doesn't try to chunk the upload but will instead stream it:
        self.len = read_limit

    def read(self, amount=-1):
        if self.amount_seen >= self.read_limit:
            return b''
        remaining_amount = self.read_limit - self.amount_seen
        data = self.file_obj.read(min(amount, remaining_amount))
        self.amount_seen += len(data)
        return data

这应该大致可以作为一个好的包装对象。然后你会像这样使用它:

 with open('my_large_file', 'rb') as file_obj:
     file_obj.seek(my_offset)
     upload = FileLimiter(file_obj, my_chunk_limit)
     r = requests.post(url, data=upload, headers={'Content-Type': 'application/octet-stream'})

标头显然是可选的,但是当将数据流式传输到服务器时,作为一个体贴的用户并告诉服务器您要发送的内容的类型是一个好主意。

【讨论】:

  • 谢谢,这正是我所需要的!当amount 为-1 时,对read 的一个小错误修复:data = self.file_obj.read(remaining_amount if amount < 0 else min(amount, remaining_amount))
  • 我们如何使用这种方法将文件名发送到服务器?
  • 什么是“chucksize”本身很大,这是在多线程中。我认为调用 read() 会导致 memoryIssue ?
【解决方案2】:

我只是将其他 2 个答案放在一起,所以如果它不能开箱即用,请耐心等待 - 我无法测试这个:

Lazy Method for Reading Big File in Python?

http://docs.python-requests.org/en/latest/user/advanced/#chunk-encoded-requests

def read_in_chunks(file_object, blocksize=1024, chunks=-1):
    """Lazy function (generator) to read a file piece by piece.
    Default chunk size: 1k."""
    while chunks:
        data = file_object.read(blocksize)
        if not data:
            break
         yield data
         chunks -= 1

requests.post('http://some.url/chunked', data=read_in_chunks(f))

【讨论】:

  • 请注意,Greg 想要上传整个文件。也许您应该将chunk_size 更改为其他内容,例如blocksize,因为Greg 使用chunksize 来表示要传输的数据的总大小。 FWIW,当chunksize 字节已发送时,您的代码可以轻松修改以跳出循环,唯一的技巧是如果chunksize % blocksize 不为零,则最后一个块可能很短。
  • 我更新了示例,以便您可以指定最大块数
  • 好的。这样可行。调用者必须确保blocksizechunksize 的除数,但希望这不是主要问题。
  • 这看起来很有希望!因此,如果我还想从大文件中的某个偏移量开始,我可以在 while 语句之前在那里抛出一个 seek 吗?
  • 所以如果我对最后一个块很短没问题,我不必担心确保块大小是块大小的除数?
猜你喜欢
  • 1970-01-01
  • 2013-03-11
  • 2020-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-11
  • 1970-01-01
  • 2018-10-25
相关资源
最近更新 更多