You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的线程池里异步执行,避免阻塞主线程:
    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)
    
    然后在post方法里用await self.decode_image(img_data)来调用。

内容的提问来源于stack exchange,提问作者Apoorva Singh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 21:22:54