使用HTTP分块编码上传文件时遭遇SSL错误
尝试用分块编码上传文件时遇到以下SSL错误:
urllib3.exceptions.SSLError: EOF occurred in violation of protocol (_ssl.c:2423)
初始实现代码:
from io import BytesIO import requests from werkzeug.datastructures import FileStorage def test_upload_large_file(): file_data = BytesIO(b'0' * (25 * 1024 * 1024)) file = FileStorage(stream=file_data, filename='test.txt') headers = {'Transfer-Encoding': 'chunked'} url = "<url>" response = requests.post(url, headers=headers, files={'file': file}) print(response.status_code) assert response.status_code == 200 test_upload_large_file()
改用Content-Type: multipart/form-data后上传正常,但重写本地文件读取的分块上传代码仍报相同错误:
import requests def upload_file_chunked(url, file_path, chunk_size): with open(file_path, 'rb') as file: headers = {'Transfer-Encoding': 'chunked'} response = requests.post(url, headers=headers, data=file, stream=True) print("Response Code:", response.status_code) print("Response:", response.text) # Example usage url = "<url>" file_path = "<file-path>" chunk_size = 1024 # Specify the desired chunk size in bytes upload_file_chunked(url, file_path, chunk_size)
问题根源分析
requests库自动处理逻辑冲突
当你手动设置Transfer-Encoding: chunked同时使用files参数时,requests会自动生成multipart/form-data类型的请求。手动添加的分块编码头会和库的自动处理逻辑冲突,导致请求格式畸形。服务器无法解析这种异常请求,会提前关闭连接,最终触发SSL层的EOF错误(这是表象,本质是请求格式不符合预期)。分块编码与multipart/form-data的兼容性问题
虽然HTTP/1.1标准支持分块编码,但很多服务器在处理multipart/form-data请求时,并不期望叠加分块编码——multipart本身已经有边界分割机制,叠加分块会让请求结构过于复杂,部分服务器无法正确解析,直接终止连接。SSL错误的本质并非加密问题
你看到的SSL错误只是结果,核心原因是请求格式错误导致服务器提前断开SSL连接,而非证书验证、加密算法等SSL本身的问题。
解决办法
让requests自动处理分块编码
requests在处理大文件上传时,若使用stream=True并传入文件对象,会自动启用分块编码,无需手动添加Transfer-Encoding头:import requests def upload_file_chunked(url, file_path): with open(file_path, 'rb') as file: # 移除手动设置的Transfer-Encoding头 response = requests.post(url, data=file, stream=True) print("Response Code:", response.status_code) print("Response:", response.text) # 使用示例 url = "<url>" file_path = "<file-path>" upload_file_chunked(url, file_path)表单式文件上传直接用files参数
如果需要上传带文件名等元信息的表单式文件,直接使用files参数即可,requests会自动处理大文件的分块逻辑:from io import BytesIO import requests from werkzeug.datastructures import FileStorage def test_upload_large_file(): file_data = BytesIO(b'0' * (25 * 1024 * 1024)) file = FileStorage(stream=file_data, filename='test.txt') url = "<url>" # 移除手动设置的Transfer-Encoding头 response = requests.post(url, files={'file': file}) print(response.status_code) assert response.status_code == 200 test_upload_large_file()确认服务器支持的上传格式
部分服务器可能仅接受multipart/form-data格式的文件上传,不支持原始分块上传。如果必须用分块编码上传原始文件,需确认服务器是否接受该格式,必要时添加Content-Type: application/octet-stream头。
内容的提问来源于stack exchange,提问作者saurav

