Python2.7 CKAN环境下AJAX上传文件cgi.FieldStorage为空问题求助
问题根因
- CKAN中间件提前消费了请求流
CKAN默认WSGI中间件会预先读取wsgi.input的内容解析请求参数,读取完成后会将流指针移到末尾,同时清空CONTENT_LENGTH字段值,这是你读取CONTENT_LENGTH始终返回0的直接原因。 - 自定义读流逻辑依赖失效的CONTENT_LENGTH
因为CONTENT_LENGTH为0,你的read_body方法不会进入读流循环,返回的临时文件为空,传给cgi.FieldStorage后自然解析不到任何字段。 - wsgi.input已被CKAN封装
从日志可见wsgi.input已经被封装为FakeCGIBody对象,不是原始的请求流,直接读取该对象无法拿到原始multipart格式数据。 - 前端jQuery版本过低不支持FormData上传配置
jQuery 1.4版本未提供contentType: false、processData: false两个配置项的支持,会导致你构造的FormData被自动序列化为普通字符串,实际请求格式不符合multipart/form-data要求,后端无法解析文件。
解决方案
后端优化(推荐)
不需要手动读流和解析,直接复用CKAN已经解析好的请求数据:
from ckan.plugins.toolkit import request import os def pop_file_upload(self, environ, start_response): upload_file = request.POST.get('file') if not upload_file: # 按业务逻辑处理无文件的异常情况 return # 保存文件 file_name = os.path.basename(upload_file.filename) save_path = os.path.join('upload', file_name) with open(save_path, 'wb') as f: f.write(upload_file.file.read())
如果必须手动解析原始请求,需要在路由注册时添加parse_body=False参数,跳过CKAN的默认请求体解析逻辑,同时修改读流逻辑不再依赖CONTENT_LENGTH:
def read_body(self, environ): stream = environ['wsgi.input'] body = TemporaryFile(mode='w+b') # 直接读满流,不依赖CONTENT_LENGTH while True: part = stream.read(200*1024) if not part: break body.write(part) body.seek(0) environ['wsgi.input'] = body return body
前端优化
将jQuery升级到1.6及以上版本,保证contentType: false和processData: false配置生效;如果不能升级jQuery,改用原生XMLHttpRequest发送FormData请求,避免低版本jQuery的兼容问题。
排查提示
- 打印完整的请求头日志,确认
CONTENT_TYPE是否为multipart/form-data,CONTENT_LENGTH是否存在 - 读流完成后先打印临时文件的大小,确认是否真的读取到了请求体内容
- 确认自定义接口的路由配置,检查是否开启了默认的请求体解析
- 用抓包工具确认前端实际发送的请求格式和内容是否符合预期
内容的提问来源于stack exchange,提问作者Cha
相关产品推荐
相关产品推荐

