使用@tornado.web.stream_request_body上传大文件异常求助
解决Tornado流式上传大文件后文件损坏的问题
我之前也碰到过一模一样的情况——用stream_request_body传大文件后,PDF之类的二进制文件直接打不开,大概率是文件写入不完整、分块处理不当或者资源没正确释放导致的。结合你的代码片段,我整理几个关键排查点和修复方案:
一、先把流式上传的核心逻辑补全
你的代码只提到了打开文件,但流式上传必须在data_received里逐块写入,还要在请求结束时确保文件关闭,不然很容易出现文件截断:
import os import tornado.web MAX_STREAMED_SIZE = 1024 * 1024 * 1024 # 1GB @tornado.web.stream_request_body class UploadHandler(tornado.web.RequestHandler): def prepare(self): # 设置单个请求的最大体大小 self.request.connection.set_max_body_size(MAX_STREAMED_SIZE) # 确保上传目录存在,避免报错 upload_dir = "./uploads" if not os.path.exists(upload_dir): os.makedirs(upload_dir) # 用二进制模式打开文件!这是二进制文件(PDF)不损坏的关键 self.file_path = os.path.join(upload_dir, "uploaded_file.pdf") self.f = open(self.file_path, "wb") # 可选:记录已接收字节数,用于后续校验 self.received_bytes = 0 self.expected_bytes = int(self.request.headers.get("Content-Length", 0)) def data_received(self, chunk): # 逐块写入字节流,不要做任何编码转换 self.received_bytes += len(chunk) self.f.write(chunk) def post(self): # 请求完成后先校验文件完整性 if self.expected_bytes > 0 and self.received_bytes != self.expected_bytes: os.remove(self.file_path) self.set_status(400) self.write({"error": "文件上传不完整,请重试"}) return # 关闭文件句柄 self.f.close() self.write({"status": "success", "file_path": self.file_path}) def on_finish(self): # 兜底处理:即使请求异常中断,也要确保文件被关闭 if hasattr(self, 'f') and not self.f.closed: self.f.close()
这里一定要注意:
- 打开文件必须用
"wb"二进制模式,用文本模式会因为编码规则破坏PDF的二进制结构,直接导致文件损坏。 data_received会被多次调用,每次传入的chunk是原生字节块,直接写入即可,别做任何decode之类的操作。
二、确认全局请求大小限制没拖后腿
有时候你在handler里设置了set_max_body_size,但Tornado应用的全局max_request_size如果更小,会直接截断请求,导致文件不完整。记得在应用初始化时同步设置全局限制:
app = tornado.web.Application( [ (r"/upload", UploadHandler), ], max_request_size=MAX_STREAMED_SIZE # 和handler里的限制保持一致 )
三、先拿小文件验证基础逻辑
如果大文件还是有问题,先传个几MB的PDF试试:
- 如果小文件能正常打开,说明核心逻辑没问题,问题出在大文件的流式处理或网络中断上;
- 如果小文件也损坏,那大概率是文件打开模式或者写入方式错了,重点检查
open的参数和data_received里的写入逻辑。
内容的提问来源于stack exchange,提问作者Senya Marks
相关产品推荐
相关产品推荐

