我正在构建一个文件共享 Web 应用程序,我想添加一些功能,这些功能与动态创建文件和目录以及临时提供内容直到请求完全结束
我已经阅读了文档中的 StreamResponse 和 Response 类以及源代码中的 FileResponse 类,我发现唯一可能相关的是 StreamResponse 的“任务”参数,但不太清楚如何使用它
我知道这是一个异步任务,但是否必须在返回响应之前创建它?
它是否在响应结束时以异步收集方式运行?
我看对地方了吗?
按照文档中的示例,这里是执行此操作的函数。
background_tasks = set()
def do_in_background (result_of_async_call):
task = asyncio.create_task(result_of_async_call)
background_tasks.add(task)
task.add_done_callback(background_tasks.discard)
现在代替:
await foo(bar)
你有
do_in_background(foo(bar))
这里到底发生了什么?
asyncio
有一个事件调度程序。它不断地做事。检查传入请求、处理请求等。任务是它将要做的事情。但事件调度程序只为其任务保留一个“weakref”。如果它们没有从其他地方被提及,它们就会消失。aiohttp 服务器保留对实际 Web 请求的强引用,直到得到响应。然后把它扔掉。因此,如果您只是创建一个任务,它会在提供文件时停止运行。正如您可能发现的那样。所以我们在
set
的background_tasks
中保持强引用。这将使任务保持活动状态,直到任务完成、调用回调并销毁引用。
结果是,您现在可以安排异步工作在 Web 请求完成后在后台运行。
这是后续问题。
async def handle(request):
tg = asyncio.TaskGroup()
...
name = request.match_info.get('name', "Anonymous")
...
tg.create_task(some_coro(...))
tg.create_task(another_coro(...))
...
cancel_task = tg.create_task(asyncio.sleep(100))
cancel_task.cancel() # Will cancel the whole task group.
async with tg:
pass # Will wait for the group to actually finish cancelling.
return web.Response(text=text)
我终于通过直接修改aiohttp的源码解决了这个问题
我所做的是向 FileResponse 类添加一个附加变量,我将其命名为“delete_file_after_sending”,一个 bool。然后在返回 writer 之前的 _sendfile() 方法中我添加了以下内容:
if self.delete_file_after_sending:
self._path.unlink()
这将删除提供给 FileResponse 的路径然后在我的代码中我测试了一个像这样的大文件:
from pathlib import Path
from aiohttp import web
...
async def download_test(request):
...
test_file=Path("./some-big-movie.mkv")
content_disposition=f"attachment; filename=\"{test_file.name}\""
return web.FileResponse(
path=fse_serverside,
headers={
"content-disposition":content_disposition,
"content-length":content_length
},
chunk_size=1048576,
delete_file_after_sending=True,
)
...
它按预期工作:大文件正确发送到客户端,并在服务器端立即删除我使用不同的设备作为我的计算机的客户端在本地网络上对此进行了测试。当然,我还需要做一些其他测试,例如取消下载、在 VPS 上测试等...