如何在Tornado中正确收发字节流(含JPG传输场景)
正确在Tornado中收发二进制字节流(以JPG图片为例)
这个问题我之前也踩过坑,核心问题是你把二进制数据当成文本处理了,导致原始字节被不可逆地破坏了。咱们拆解成服务端(Tornado)和客户端(requests)两部分来解决:
问题根源
JPG是二进制文件,不是UTF-8编码的文本。当你用requests.get(url).text接收时,requests会尝试把响应字节按默认编码(通常是UTF-8)解码成字符串——但图片字节里大量存在不符合UTF-8规则的字节,这些字节会被替换成Unicode替换字符�(对应字节\xef\xbf\xbd),之后再转码回去自然和原内容不一样了。
一、Tornado端:正确返回二进制数据
不管是整体返回还是分块发送,核心是告诉客户端“这是二进制数据”,并直接发送原始字节。
1. 整体返回图片字节
直接设置正确的Content-Type,然后用self.write()写入原始字节即可:
import tornado.web import tornado.ioloop class ImageHandler(tornado.web.RequestHandler): def get(self): # 读取原始二进制内容 with open('file.jpg', 'rb') as f: image_content = f.read() # 告诉客户端这是JPG图片 self.set_header('Content-Type', 'image/jpeg') # 直接发送二进制字节 self.write(image_content) # 注册路由并启动服务 app = tornado.web.Application([ (r'/get-image', ImageHandler), ]) if __name__ == '__main__': app.listen(8888) tornado.ioloop.IOLoop.current().start()
2. 分块发送字节流
如果需要分块传输(比如大文件),要开启分块编码并逐块flush:
class ChunkedImageHandler(tornado.web.RequestHandler): async def get(self): self.set_header('Content-Type', 'image/jpeg') # 开启分块传输 self.set_header('Transfer-Encoding', 'chunked') chunk_size = 1024 # 每次发送1KB块 with open('file.jpg', 'rb') as f: while True: chunk = f.read(chunk_size) if not chunk: break # 写入当前块 self.write(chunk) # 立即刷新缓冲区,发送块到客户端 await self.flush() # 结束分块传输 self.finish()
二、requests端:正确接收二进制数据
绝对不要用.text接收二进制内容,改用.content直接获取原始字节串:
import requests url = 'http://localhost:8888/get-image' response = requests.get(url) # 用.content获取原始二进制字节,和Tornado发送的完全一致 image_bytes = response.content # 可以验证:把字节写回文件,和原图片完全相同 with open('received_image.jpg', 'wb') as f: f.write(image_bytes)
关键总结
- 服务端(Tornado):返回二进制时必须设置匹配的
Content-Type,直接操作原始字节,不要转成字符串;分块发送需开启Transfer-Encoding: chunked并逐块flush。 - 客户端(requests):二进制内容用
.content获取,.text只用于接收文本类响应(比如JSON、HTML)。
内容的提问来源于stack exchange,提问作者user82395214
相关产品推荐
相关产品推荐

