• zipfile 解压文件名乱码


    zipfile 中文文件名 解压乱码

    上传文件功能模块需求及BUG现象:

    环境

    mac

    django 1.11.13

    python 3.6

    功能需求:

    上传一个.zip格式的压缩文件

    解压该test.zip压缩文件

    解压zip文件时,遍历其目录下所有子文件,同时计算出单个子文件的有效代码行数

    这时,发现解压后的子文件名中文出现乱码,如下图:

    BUG截图

    解决思路

    1、解压过程中,发现解压的文件内容正常;

    2、使用的是第三方库zipfile模块,因为第1步得到正常的文件内容,本地业务逻辑可先不排查;

    3、首先检查zipfile的源码中,针对编码/解码的执行过程仔细排查发现:

    zipfile中根据文件 flag 检测的时候,只支持 cp437 和 utf-8

    找到下面两处,并追加修正后,乱码现象解决:(追加的decode编码可根据实际情况修改,如win环境下乱码采用.decode('gbk')

    # zipfile.py
    
    # 第一处
    if flags & 0x800:
        # UTF-8 file names extension
        filename = filename.decode('utf-8')
    else:
        # Historical ZIP filename encoding
        filename = filename.decode('cp437')
        # 追加此句
        filename = filename.encode("cp437").decode('utf-8')
    
    # 第二处
    if zinfo.flag_bits & 0x800:
        # UTF-8 filename
        fname_str = fname.decode("utf-8")
    else:
        fname_str = fname.decode("cp437")
            # 追加此句
        fname_str = fname_str.encode("cp437").decode('utf-8')

    解决后,正常显示:


    上传功能源码

    import zipfile
    
    # 指定想要统计的文件类型
    whitelist = ['py']
    
    
    # 遍历文件, 递归遍历文件夹中的所有
    def getFile(basedir):
        
    #
    存储上传解压后的文件列表
        filelists = []
        for parent, dirnames, filenames in os.walk(basedir):
            # for dirname in dirnames:
            #    getFile(os.path.join(parent,dirname)) #递归
            for filename in filenames:
                ext = filename.split('.')[-1]
                # 只统计指定的文件类型,略过一些log和cache文件
                if ext in whitelist:
                    filelists.append(os.path.join(parent, filename))
    
    
    # 统计一个文件的行数
    def countLine(fname):
        count = 0
        single_quotes_flag = False
        double_quotes_flag = False
        with open(fname, 'rb') as f:
            for file_line in f:
                file_line = file_line.strip()
                # print(file_line)
                # 空行
                if file_line == b'':
                    pass
    
                # 注释 # 开头
                elif file_line.startswith(b'#'):
                    pass
    
                # 注释 单引号 ''' 开头
                elif file_line.startswith(b"'''") and not single_quotes_flag:
                    single_quotes_flag = True
                # 注释 中间 和 ''' 结尾
                elif single_quotes_flag == True:
                    if file_line.endswith(b"'''"):
                        single_quotes_flag = False
    
                # 注释 双引号 """ 开头
                elif file_line.startswith(b'"""') and not double_quotes_flag:
                    double_quotes_flag = True
                # 注释 中间 和 """  结尾
                elif double_quotes_flag == True:
                    if (file_line.endswith(b'"""')):
                        double_quotes_flag = False
    
                # 代码
                else:
                    count += 1
    
            # print(fname + '----', count)
            #   单个文件行数
            print(fname, '----count:', count)
            return count
    
    
    def un_zip(file_name):
        """unzip zip file"""
        zip_file = zipfile.ZipFile(file_name)
        # <zipfile.ZipFile filename='/Users/limengjie/Desktop/pyhon/SMS0614/upload_file/0617.zip' mode='r'>
        if os.path.isdir(file_name + "_files"):
            pass
        else:
            os.mkdir(file_name + "_files")
        for names in zip_file.namelist():
            zip_file.extract(names, file_name + "_files/")
        # 遍历解压后得到的文件夹, 递归遍历文件夹中的所有子文件
        getFile(file_name + "_files")
        totalline = 0
        # 遍历解压后的文件列表,统计单个文件的行数并汇总
        for filelist in filelists:
            totalline = totalline + countLine(filelist)
        zip_file.close()
        # 返回上传文件所有子文件的总行数
        return totalline

    补充:上传业务逻辑代码

    class Uploading(View):
    
        def get(self, request):
            return render(request, "uploading.html", )
    
        def post(self, request):
            # 1、拿到压缩文件对象file_obj
            file_obj = request.FILES.get("user_file")
            file_name = os.path.join(file_dir, file_obj.name)
            file_size = file_obj.size
            with open(file_name, "wb") as f:
                for line in file_obj.chunks():
                    f.write(line)
    
            # 2、解压压缩文件,并获取代码行数属性
            total_line = un_zip(file_name)
            # 3、单个文件进行文件对象实例化,文件名,文件大小,代码行数
            models.FileObj.objects.create(
                fileName=file_obj.name,
                fileSize=file_size,
                fileLineCount=total_line
            )
            return redirect("/upload_file/")

    优化需求

    统计行数优化:mac环境解压文件时,系统会自动追加__MACOSX文件夹,为了不遍历此文件夹,需补充:

    在getFIle函数中修改,即可:

    # MAC环境下略过__MACOSX文件夹
            if "__MACOSX" in dirnames:
                pop_index = dirnames.index("__MACOSX")
                dirnames.pop(pop_index)

    优化后,得到我们需要的结果:

    (完) 

  • 相关阅读:
    V2热帖:要多健壮的代码才能支撑起千变万化的需求?
    jmeter生成html报告的命令
    jmeter5.x&4.x搭配使用Serveragent 监听服务端性能参数
    springboot关于tomcat的几个默认配置
    nginx日志统计分析-shell
    OpenStack虚拟机VIP配置步骤
    openstack 3.14.3 虚拟机增加指定IP网卡
    OpenStack各组件的常用命令
    Filebeat的Registry文件解读
    一个shell脚本的实践
  • 原文地址:https://www.cnblogs.com/limengjie0104/p/9192449.html
Copyright © 2020-2023  润新知