Tornado流式分块上传时data_received异常如何正确关闭文件对象
问题根因
启用stream_request_body装饰器后,data_received方法由Tornado底层的连接处理逻辑直接调用,一旦抛出未捕获的异常,会直接中断整个请求流程,不会执行后续的post方法,因此post方法中定义的文件关闭逻辑无法触发,导致文件句柄泄漏。
解决方案
方案1:使用on_finish钩子作为兜底资源释放(推荐)
Tornado的RequestHandler内置的on_finish钩子会在请求生命周期的最后阶段统一执行,无论请求过程是否出现异常,都会被调用,是释放资源的最优位置。
修改后的完整代码如下:
from tornado.ioloop import IOLoop from tornado.web import Application, RequestHandler, stream_request_body import os # 可选,用于异常场景下清理不完整文件 @stream_request_body class UploadHandler(RequestHandler): def prepare(self): self.file = open('uploaded_file', 'wb') self.upload_success = False # 可选,用于标记上传是否完成 def data_received(self, chunk): print(len(chunk)) self.file.write(chunk) # 强制抛出异常测试 raise Exception('Error') def post(self, *args, **kwargs): self.upload_success = True self.write('UPLOADED') def on_finish(self): # 兜底关闭文件,所有场景都能覆盖 if hasattr(self, 'file') and not self.file.closed: self.file.close() # 可选逻辑:上传失败时删除不完整的临时文件 if not self.upload_success and os.path.exists('uploaded_file'): os.remove('uploaded_file') app = Application([(r'/upload', UploadHandler), ], debug=True) if __name__ == '__main__': app.listen(7777) IOLoop.current().start()
该方案侵入性极低,不需要修改现有上传业务逻辑,所有资源释放逻辑统一管理,不会出现遗漏。
方案2:在data_received内捕获异常主动处理
如果需要针对上传过程的异常给客户端返回明确的错误响应,避免客户端出现连接断开的报错,可以在data_received内部新增异常捕获逻辑:
def data_received(self, chunk): try: print(len(chunk)) self.file.write(chunk) # 强制抛出异常测试 raise Exception('Error') except Exception as e: # 先关闭已打开的文件 if hasattr(self, 'file') and not self.file.closed: self.file.close() # 返回错误响应给客户端 self.set_status(500) self.write(f"上传失败:{str(e)}") # 中断后续请求处理流程 self.finish()
两种方案可以搭配使用,on_finish作为兜底的资源释放逻辑,data_received内的异常捕获做业务层的错误响应处理,双重保障文件资源不会泄漏。
内容的提问来源于stack exchange,提问作者jonas
相关产品推荐
相关产品推荐

