【问题标题】:Python Requests Stream AsyncPython 请求流异步
【发布时间】:2018-12-20 15:41:26
【问题描述】:

我正在尝试通过设置 stream=True

从 python 请求库下载大文件

但我希望这个函数异步执行,并将响应发送回服务器并在后台下载。

这是我的代码

async def downloadFile(url, filename):
  r = requests.get(url, stream=True)
  with open(os.path.join('./files', filename), 'wb+') as f:
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:
            f.write(chunk)
  # Creating same file name
  # with _done appended to know that file has been downloaded
  with open(os.path.join('./files', filename + '_done'), 'w+') as f: 
    f.close()
  await asyncio.sleep(1)

像这样从其他函数调用这个函数

# check if file exist in server
        if(os.path.exists(os.path.join('./files', fileName))):

            #file exist!!!

            #check if done file exist
            if(os.path.exists(os.path.join('./files', fileName + '_done'))):

                #done file exist
                self.redirect(self.request.protocol + "://" +
                              self.request.host + '/files/' + fileName)
            else:
                #done file not exist. Wait for 5 min more

                self.write('Wait 5 min')
                self.finish()
        else:
            # file doesnt exist. Initiate download
            self.write('Wait 5 min')
            self.finish()
            d = asyncio.ensure_future(downloadFile(
                fileRes, fileName))
            # loop = asyncio.get_event_loop()
            # loop.run_until_complete(d)

问题是文件已创建,但其大小仍为 0,并且从未创建附加“_done”的文件。 我在这里做错了什么?

【问题讨论】:

  • 您的代码对我有用。也许enable debugging for requests?
  • 你的代码也适合我
  • 此代码仅适用于第一次。如果我立即发起请求,它会等待整个文件下载。我已经用有关如何调用下载功能的详细信息更新了我的问题。请检查。

标签: python python-3.x python-requests python-asyncio


【解决方案1】:

您的代码对我有用。也许是您尝试获取的资源不起作用。

您可能想按照@brennan 的建议尝试enabling debug for requests,和/或将打印输出添加到您的代码中以了解正在发生的事情:

>>> import requests
>>> import asyncio
>>> 
>>> 
>>> async def downloadFile(url, filename):
...   print(f"• downloadFile({url}, {filename})")
...   r = requests.get(url, stream=True)
...   print(f" → r: {r}")
...   with open(os.path.join('./files', filename), 'wb+') as f:
...     print(f" → f is opened: {f}")
...     for chunk in r.iter_content(chunk_size=1024):
...         print(f"  → chunk is: {chunk}")
...         if chunk:
...             f.write(chunk)
...   # Creating same file name
...   # with _done appended to know that file has been downloaded
...   with open(os.path.join('./files', filename + '_done'), 'w+') as f:
...     print(f" → creating output with _done")
...     f.close()
...   print(f" → wait 1")
...   await asyncio.sleep(1)
... 
>>> 
>>> 
>>> d = asyncio.ensure_future(downloadFile('https://xxx/yyy.jpg', 'test.jpg'))
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(d)
• downloadFile(https://xxx/yyy.jpg, test.jpg)
 → r: <Response [200]>
 → f is opened: <_io.BufferedRandom name='./files/test.jpg'>
  → chunk is: b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\r\t\n\x0b\n\x08\r\x0b\n\x0b\x0e\x0e\r\x0f\x13....'
  → chunk is: ...
  ...
 → creating output with _done
 → wait 1

这将使您的代码中的_done 部分无用(您只需要打印输出)。甚至最后的等待(当它完成时......它完成了!)。

async def downloadFile(url, filename):
  r = requests.get(url, stream=True)
  with open(os.path.join('./files', filename), 'wb+') as f:
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:
            f.write(chunk)

尽管您可能想捕捉连接到服务器时发生的任何可能的问题并采取相应措施:

async def downloadFile(url, filename):
  try:
    r = requests.get(url, stream=True)
    r.raise_for_status() # to raise on invalid statuses
    with open(os.path.join('./files', filename), 'wb+') as f:
      for chunk in r.iter_content(chunk_size=1024):
          if chunk:
              f.write(chunk)
  except requests.RequestException as err:
    # do something smart when that exception occurs!
    print(f"Exception has occured: {err}")

【讨论】:

  • 实际上这只适用于第一次。但是,如果我立即再次请求,它会等待响应,直到文件下载完成。这是工作流程 1- 在脚本中搜索文件的脚本。如果找到它返回,如果没有,它在向用户发送响应“等待 5 分钟”后开始在服务器中下载 2-如果文件已下载并且存在“_done”文件。然后它将文件发送给用户请求。 3- 如果文件存在但不存在“_done”文件。然后文件必须在服务器中下载。并将响应发送回用户等待 5 分钟。
  • 我更新了我的问题,详细说明了如何启动下载。
  • 好的,TBH,我不明白你的要求/你想要达到的目标......我认为你陷入了X-Y Problem 的深层案例。看起来您正试图通过蛮力下载该文件来规避下载文件的问题,直到它最终起作用。但是您可以在不使用_done 文件的情况下通过使用requests 状态更优雅地实现这一点(参见我回答中的最后一个示例)。但正确的方法主要取决于您为什么需要观看文件并重新下载。
猜你喜欢
  • 1970-01-01
  • 2019-07-28
  • 2012-04-03
  • 1970-01-01
  • 2012-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多