You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI后端处理URL编码长文件名Zip解压失败问题求助

解决Zip归档文件名URL编码+超长导致解压失败的问题

直接修改原始Zip字节串会破坏归档结构(比如文件头的长度标识、校验和),导致文件损坏。正确的做法是解析原始Zip流,重新生成一个文件名合法的新Zip归档,具体实现如下:

核心思路

  1. 将传入的字节串包装为内存类文件对象,用zipfile模块解析原始归档
  2. 对每个文件的URL编码文件名进行解码
  3. 处理超长文件名(截断+哈希后缀避免重名),同时清理非法字符
  4. 将处理后的文件写入新的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,提问作者Илья Олейников

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 01:13:35