基于Python http.server模块实现POST文件上传功能的问题求解
问题解决与代码优化方案
一、self.rfile.readlines() 卡住的根因与解决方案
卡住的核心原因是HTTP/1.1默认启用长连接,self.rfile是绑定到TCP套接字的输入流,readlines()方法会持续等待流结束信号,只有当浏览器主动断开连接(比如关闭标签页)才会停止阻塞,返回已读取的内容。
正确处理方式:
- 先从请求头中获取
Content-Length字段,仅读取指定长度的请求体,避免无限等待 - 不要手动正则解析multipart格式,直接用Python内置的
cgi.FieldStorage工具类处理表单上传,自动拆分字段、提取文件名和文件内容,避免边界匹配错误导致的文件损坏问题。
二、do_POST() 正确实现示例
import os import cgi from http.server import SimpleHTTPRequestHandler class SimpleHTTPRequestHandlerWithUpload(SimpleHTTPRequestHandler): # 已实现的list_directory方法此处省略,仅需保证表单enctype为multipart/form-data、method为POST即可 def do_POST(self): try: # 解析表单数据 form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type']} ) # 获取上传文件字段,假设表单中文件input的name属性为upload_file upload_item = form['upload_file'] if not upload_item.filename: self.send_error(400, "No file selected for upload") return # 安全处理文件名:仅保留文件名主体,防止路径遍历攻击 filename = os.path.basename(upload_item.filename) save_path = os.path.join(os.getcwd(), filename) # 写入文件 with open(save_path, 'wb') as f: f.write(upload_item.file.read()) # 校验上传结果 upload_size = upload_item.length saved_size = os.path.getsize(save_path) if upload_size != saved_size: os.remove(save_path) self.send_error(500, "File upload failed: size mismatch") return # 上传成功后重定向回目录页,避免用户刷新重复提交 self.send_response(303) self.send_header('Location', self.path) self.end_headers() except Exception as e: self.send_error(500, f"Upload error: {str(e)}")
三、其他代码优化建议
- 安全加固:除文件名路径过滤外,可新增允许上传的文件后缀白名单、单文件大小上限限制,避免恶意上传可执行文件或超大文件占用磁盘
- 重名文件处理:当前目录存在同名文件时,可自动给文件名加序号后缀(如
a.jpg改为a_1.jpg),避免直接覆盖已有文件 - 并发能力优化:默认
http.server是单线程模式,多个上传请求并发时会发生阻塞,可继承ThreadingHTTPServer实现多线程处理,提升并发承载能力 - 交互体验优化:可在目录页新增上传成功/失败的提示信息,不要仅返回纯HTTP错误码
- 权限控制:如果服务需要部署在公网,需新增身份校验逻辑,避免未授权用户随意上传文件
内容的提问来源于stack exchange,提问作者m00nlightsh4dow
相关产品推荐
相关产品推荐

