Tornado新手求助:如何用stream_request_body分块读取请求中的img_data
解决Tornado流式读取POST请求中大型
img_data数组的问题 嗨,刚接触Tornado的话,确实容易在stream_request_body的用法上踩坑——你原来的代码用get_body_argument其实会绕过流式处理,因为这个方法会等待整个请求体完全接收后才返回,完全没用到分块读取的特性,大数据量下自然会出问题。下面给你一步步讲正确的实现方式:
基础实现:先拼接分块再解析JSON
如果你的img_data数组还没大到内存扛不住的地步,先把分块数据拼接成完整的请求体,再解析处理是最直接的方案:
import json import base64 from tornado.web import BaseHandler, stream_request_body @stream_request_body class MyReportPDF(BaseHandler): def initialize(self): # 初始化一个缓冲区,用来存储分块收到的请求体数据 self.body_buffer = bytearray() def data_received(self, chunk): # 每次收到数据块,就追加到缓冲区里 self.body_buffer.extend(chunk) async def post(self): try: # 把缓冲区的字节数据解码成字符串,再解析成JSON body_json = json.loads(self.body_buffer.decode('utf-8')) img_data_list = body_json.get('img_data', []) decode_image = [] for img_data in img_data_list: # 清理base64前缀并解码图片 cleaned_base64 = img_data.replace('data:image/jpeg;base64,', '') decode_image.append(base64.b64decode(cleaned_base64)) # 这里可以添加你的业务逻辑,比如生成PDF、保存图片等 self.write({"status": "success", "processed_images": len(decode_image)}) except json.JSONDecodeError: self.set_status(400) self.write({"error": "请求体不是合法的JSON格式"}) except Exception as e: self.set_status(500) self.write({"error": f"服务器处理错误:{str(e)}"})
关键要点:
initialize方法会在每个请求到来时初始化缓冲区,避免不同请求之间的数据干扰。data_received是流式处理的核心,Tornado会自动把收到的分块数据传给这个方法,你只需要负责把块追加到缓冲区即可——注意这个方法是同步的,别在这里做耗时操作(比如解码图片),会阻塞IO线程。post方法会在所有分块接收完成后被调用,这时就可以安全地解析完整的JSON并处理数据了。
进阶优化:流式解析超大JSON数组
如果你的img_data数组特别大(比如上百张base64图片),把整个JSON加载到内存里会占用过多资源,这时候可以用流式JSON解析库来逐个读取数组元素,不用加载整个JSON:
首先安装依赖库:
pip install ijson
然后修改代码:
import base64 from io import BytesIO from tornado.web import BaseHandler, stream_request_body import ijson @stream_request_body class MyReportPDF(BaseHandler): def initialize(self): self.body_buffer = bytearray() def data_received(self, chunk): self.body_buffer.extend(chunk) async def post(self): decode_image = [] try: # 把缓冲区转换成字节流,供ijson流式解析 json_stream = BytesIO(self.body_buffer) # 逐个读取`img_data`数组里的元素,不用加载整个JSON for img_data in ijson.items(json_stream, 'img_data.item'): cleaned_base64 = img_data.replace('data:image/jpeg;base64,', '') decode_image.append(base64.b64decode(cleaned_base64)) self.write({"status": "success", "processed_images": len(decode_image)}) except ijson.JSONError: self.set_status(400) self.write({"error": "请求体不是合法的JSON格式"}) except Exception as e: self.set_status(500) self.write({"error": f"服务器处理错误:{str(e)}"})
优势:
这种方式不需要把整个JSON加载到内存,哪怕img_data有上千个元素,内存占用也会很低,适合处理超大型请求。
额外注意事项
- 确保请求的
Content-Type是application/json,否则Tornado的流式处理可能无法正确识别请求体格式。 - 如果解码图片的操作很耗时,建议把这部分逻辑放到Tornado的线程池里异步执行,避免阻塞主线程:
然后在post方法里用from tornado.concurrent import run_on_executor from concurrent.futures import ThreadPoolExecutor class MyReportPDF(BaseHandler): executor = ThreadPoolExecutor(max_workers=4) # ... 其他方法不变 ... @run_on_executor def decode_image(self, img_data): cleaned_base64 = img_data.replace('data:image/jpeg;base64,', '') return base64.b64decode(cleaned_base64)await self.decode_image(img_data)来调用。
内容的提问来源于stack exchange,提问作者Apoorva Singh
相关产品推荐
相关产品推荐

