使用Raw Socket获取Multipart/Form-Data遇UTF-8解码错误求助
解决Raw Socket处理multipart/form-data时的UnicodeDecodeError问题
这个问题我之前也碰到过!核心原因很简单:当表单包含文件上传字段时,整个请求体是文本和二进制数据的混合体——文本字段(比如username、password)是UTF-8编码的,但文件内容是原始二进制(比如PNG的开头字节0x89就不是合法的UTF-8字符),直接把整个字节流用decode('utf-8')转成字符串肯定会报错。
下面给你一步步的解决方案:
1. 先保留原始字节数据,不要立刻解码
把你原来的代码:
request = conn.recv(10240).decode()
改成:
raw_data = conn.recv(10240) # 保留字节类型,不急于解码
注意:实际生产环境中,一次recv可能无法获取完整的请求体,需要循环接收直到读取完所有数据(可以通过Content-Length头判断总长度)。
2. 从请求头中提取multipart的边界标识(boundary)
multipart/form-data的请求头里会包含一个boundary参数,用来分隔不同的表单字段。我们需要先解析这个边界:
# 分割请求头和请求体(用\r\n\r\n作为分隔符) header_part, body_part = raw_data.split(b'\r\n\r\n', 1) # 解析请求头,提取boundary boundary = None headers = header_part.decode('utf-8').split('\r\n') for header in headers: if header.startswith('Content-Type: multipart/form-data'): # 提取boundary字符串,转成字节类型 boundary_str = header.split('boundary=')[1] boundary = b'--' + boundary_str.encode('utf-8') break
3. 按边界分割各个表单字段,分别处理文本和文件
拿到boundary后,就可以把请求体分割成一个个独立的字段部分,然后针对文本字段和文件字段做不同处理:
import re # 分割请求体为各个字段部分 parts = body_part.split(boundary) for part in parts: if not part.strip(): # 跳过空的分割部分 continue # 分割每个字段的头部和内容 part_header, part_content = part.split(b'\r\n\r\n', 1) # 去掉内容末尾多余的\r\n和--标记 part_content = part_content.rstrip(b'\r\n--') # 解析字段头部,判断是文本还是文件 part_header_str = part_header.decode('utf-8') if 'filename=' in part_header_str: # 这是文件字段,直接处理二进制内容 filename_match = re.search(r'filename="(.+)"', part_header_str) if filename_match: filename = filename_match.group(1) # 保存文件到本地 with open(filename, 'wb') as f: f.write(part_content) print(f"成功保存文件: {filename}") else: # 这是文本字段,解码成字符串 name_match = re.search(r'name="(.+)"', part_header_str) if name_match: field_name = name_match.group(1) field_value = part_content.decode('utf-8').strip() print(f"{field_name}: {field_value}")
关键注意事项
- 处理大文件时,不要一次性把整个请求体加载到内存,应该分块接收并写入文件;
- 不同浏览器生成的boundary格式可能略有差异,但核心逻辑都是通过
--boundary来分隔字段; - 要注意请求头中的
Content-Length,确保接收完所有的请求数据,避免解析不完整。
内容的提问来源于stack exchange,提问作者Milan Vjestica
相关产品推荐
相关产品推荐

