aiohttp服务端如何在返回FileResponse后删除对应临时文件
可行实现方案
方案1:使用请求自带的完成回调钩子
aiohttp 的 Request 对象内置了 add_finish_callback 方法,注册的回调会在响应完全发送到客户端、请求生命周期结束后执行,刚好匹配你的需求。
修改后的代码示例:
import os async def getBackup(self, request): context = request.rel_url.query['sn'] filePath = createBackup(context) # 注册请求结束后的回调,忽略文件不存在、占用类异常 def clean_temp_file(_): try: if os.path.exists(filePath): os.unlink(filePath) except OSError: pass request.add_finish_callback(clean_temp_file) # 注意:原代码的CONTENT_DISPOSITION里attachement拼写错误,正确是attachment return web.FileResponse( path=filePath, headers={ hdrs.CONTENT_TYPE: 'application/tar+gzip', hdrs.CONTENT_DISPOSITION: f"attachment;filename=backup.tar.gz;filename*=UTF-8''backup.tar.gz;", hdrs.CACHE_CONTROL: 'no-cache' } )
这个方案的优势是无需修改响应类,代码侵入性极低,是最推荐的实现方式。
方案2:子类化FileResponse自定义清理逻辑
如果多个接口都有临时文件返回后清理的需求,可以自定义继承自FileResponse的子类,重写write_eof方法,在响应内容完全写入后执行清理:
import os from aiohttp import web class AutoDeleteFileResponse(web.FileResponse): async def write_eof(self) -> None: # 先执行父类的写入逻辑,确保文件内容完全发送 await super().write_eof() # 执行文件清理,忽略异常 try: if os.path.exists(self._path): os.unlink(self._path) except OSError: pass
使用时直接返回AutoDeleteFileResponse实例即可:
async def getBackup(self, request): context = request.rel_url.query['sn'] filePath = createBackup(context) return AutoDeleteFileResponse( path=filePath, headers={ hdrs.CONTENT_TYPE: 'application/tar+gzip', hdrs.CONTENT_DISPOSITION: f"attachment;filename=backup.tar.gz;filename*=UTF-8''backup.tar.gz;", hdrs.CACHE_CONTROL: 'no-cache' } )
注意事项
- Windows平台下如果文件被占用时删除会触发异常,所以清理逻辑一定要加异常捕获,避免影响请求正常结束
- 如果备份文件较大,可以额外加兜底清理逻辑,比如定时任务删除目录下超过N小时的临时文件,避免极端情况下(比如客户端中途断开连接回调未执行)残留临时文件
- 原代码中
CONTENT_DISPOSITION的attachement拼写错误,修正为attachment才能正常触发浏览器下载行为
内容的提问来源于stack exchange,提问作者mcd
相关产品推荐
相关产品推荐

