FastAPI后端处理URL编码长文件名Zip解压失败问题求助
解决Zip归档文件名URL编码+超长导致解压失败的问题
直接修改原始Zip字节串会破坏归档结构(比如文件头的长度标识、校验和),导致文件损坏。正确的做法是解析原始Zip流,重新生成一个文件名合法的新Zip归档,具体实现如下:
核心思路
- 将传入的字节串包装为内存类文件对象,用
zipfile模块解析原始归档 - 对每个文件的URL编码文件名进行解码
- 处理超长文件名(截断+哈希后缀避免重名),同时清理非法字符
- 将处理后的文件写入新的Zip归档,保证结构合法
FastAPI后端实现代码
from fastapi import FastAPI, Request import zipfile import io from urllib.parse import unquote import hashlib app = FastAPI() def fix_zip_filenames(zip_bytes: bytes) -> bytes: # 用内存IO处理,避免磁盘临时文件 input_stream = io.BytesIO(zip_bytes) output_stream = io.BytesIO() with zipfile.ZipFile(input_stream, 'r') as src_zip, zipfile.ZipFile(output_stream, 'w', zipfile.ZIP_DEFLATED) as dest_zip: for file_info in src_zip.infolist(): # 解码URL编码的文件名 decoded_name = unquote(file_info.filename) # 处理超长文件名:截断+哈希后缀,保证长度合规且不重复 max_valid_len = 255 # 兼容多数系统的文件名长度限制 if len(decoded_name) > max_valid_len: # 拆分文件名和扩展名 name, ext = decoded_name.rsplit('.', 1) if '.' in decoded_name else (decoded_name, '') # 生成短哈希值作为唯一标识 name_hash = hashlib.md5(name.encode()).hexdigest()[:8] # 计算可保留的文件名长度,拼接成新文件名 available_len = max_valid_len - len(name_hash) - 1 - len(ext) truncated_name = f"{name[:available_len]}_{name_hash}.{ext}" if ext else f"{name[:available_len]}_{name_hash}" fixed_name = truncated_name else: fixed_name = decoded_name # 清理系统非法字符(可选,避免跨平台解压问题) invalid_chars = '<>:"/\\|?*' for char in invalid_chars: fixed_name = fixed_name.replace(char, '_') # 将原始文件内容写入新归档,使用修正后的文件名 dest_zip.writestr(fixed_name, src_zip.read(file_info.filename)) output_stream.seek(0) return output_stream.read() @app.post("/handle-zip") async def handle_zip_upload(request: Request): # 接收POST请求的字节串 raw_zip_data = await request.body() # 处理Zip文件名问题 fixed_zip_data = fix_zip_filenames(raw_zip_data) # 保存处理后的Zip文件(也可直接返回给客户端) with open("fixed_archive.zip", 'wb') as f: f.write(fixed_zip_data) return {"status": "success", "msg": "Zip归档文件名已修正,可正常解压"}
关键说明
- 为什么直接改字节串会损坏:Zip归档的文件头包含文件名长度、CRC校验和等元信息,直接修改文件名字节会导致元信息不匹配,归档结构失效。
- 文件名处理逻辑可自定义:如果不需要哈希后缀,也可以直接截断,但要注意重名风险;非法字符替换可根据目标系统调整。
- 内存IO处理:全程在内存中完成解析和生成,比磁盘临时文件更高效,适合高并发场景。
内容的提问来源于stack exchange,提问作者Илья Олейников
相关产品推荐
相关产品推荐

