Python上传大文件至第三方ResApi服务器遇413错误,添加Expect:100-continue后触发连接中止问题求助
Expect:100-continue导致的连接中止问题 根据你的描述,Perl和C#代码通过添加Expect:100-continue头部就能成功上传大文件,但Python实现却出现连接中止错误,这大概率是因为requests库与自定义SSL Adapter的交互,以及MultipartEncoder的流式传输方式,干扰了100-continue的正常握手流程。以下是几个针对性的解决方案:
方案1:修改SSLAdapter,显式启用100-continue支持
urllib3的PoolManager默认启用expect_100_continue,但自定义SSLAdapter时可能没有传递这个参数,导致服务器返回100-continue后客户端无法正确响应。修改你的SSLAdapter类:
class SSLAdapter(HTTPAdapter): '''An HTTPS Transport Adapter that uses an arbitrary SSL version.''' def __init__(self, ssl_version=None, **kwargs): self.ssl_version = ssl_version super(SSLAdapter, self).__init__(**kwargs) def init_poolmanager(self, connections, maxsize, block=False): self.poolmanager = PoolManager( num_pools=connections, maxsize=maxsize, block=block, ssl_version=self.ssl_version, expect_100_continue=True # 显式启用100-continue支持 )
然后确保在请求中正确添加Expect头部(添加到Session层面更可靠):
s = Session() s.headers.update({'Expect': '100-continue', 'Accept': 'application/json'}) s.mount('https://', SSLAdapter(ssl.PROTOCOL_TLSv1_2))
方案2:改用requests原生的files参数,避免手动构造MultipartEncoder
你当前使用MultipartEncoder实现流式上传,但requests原生的files参数会自动处理大文件的100-continue逻辑,兼容性更好。重构sendFile方法:
def sendFile(self, my1GBFile): self.base_url = 'https://myserver.com/fileBoxServer/api/ssl/files/upload'; s = Session() s.headers.update({'Expect': '100-continue', 'Accept': 'application/json'}) s.mount('https://', SSLAdapter(ssl.PROTOCOL_TLSv1_2)) # 使用requests原生files参数,自动处理multipart和100-continue with open(my1GBFile, 'rb') as file_obj: file_data = { 'file': (os.path.basename(my1GBFile), file_obj, 'text/plain') } print('Sending filebox...') r = s.post( self.base_url, files=file_data, verify=self.ca_file, cert=(self.cert_file, self.key_file), timeout=54000 ) if r.ok: cookies = dict(r.cookies) print("cookies: ", cookies) else: print("Error: HTTP %i - %s, Message %s" % (r.status_code, r.reason, r.text))
这种方式会让requests自动处理请求体的分块和100-continue握手,避免流式传输带来的兼容性问题。
方案3:升级依赖库到最新兼容版本
旧版本的requests或urllib3可能存在Expect:100-continue的处理bug,尤其是Python3.8.5搭配的旧版本依赖。执行以下命令升级:
pip install --upgrade requests requests-toolbelt urllib3
升级后再测试原代码,很多时候这类兼容性问题会被新版本修复。
方案4:禁用请求预处理的头部覆盖
如果手动添加的Expect头部被requests自动覆盖,可以尝试在构造Request时禁用自动处理:
req = Request('POST', self.base_url, data=multipart_data, headers={ 'Content-Type': multipart_data.content_type, 'Accept': 'application/json', 'Expect': '100-continue' }) prepped = req.prepare() # 确保Expect头部没有被移除或修改 prepped.headers['Expect'] = '100-continue'
然后再发送预处理后的请求。
为什么Perl/C#可以正常工作?
Perl的LWP和C#的HttpClient对Expect:100-continue的处理更直接,且不会像requests那样在自定义Adapter时丢失相关配置,同时它们的multipart请求实现与100-continue握手的兼容性更好。
内容的提问来源于stack exchange,提问作者Josue Medrano

