zipfile 解壓文件名亂碼

zipfile 中文文件名 解壓亂碼

上傳文件功能模塊需求及BUG現象:

環境

machtml

django 1.11.13python

python 3.6django

功能需求:

上傳一個.zip格式的壓縮文件app

解壓該test.zip壓縮文件函數

解壓zip文件時,遍歷其目錄下全部子文件,同時計算出單個子文件的有效代碼行數post

這時,發現解壓後的子文件名中文出現亂碼,以下圖:優化

BUG截圖

解決思路

一、解壓過程當中,發現解壓的文件內容正常;編碼

二、使用的是第三方庫zipfile模塊,由於第1步獲得正常的文件內容,本地業務邏輯可先不排查;spa

三、首先檢查zipfile的源碼中,針對編碼/解碼的執行過程仔細排查發現:3d

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):
        # 一、拿到壓縮文件對象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)

        # 二、解壓壓縮文件,並獲取代碼行數屬性
        total_line = un_zip(file_name)
        # 三、單個文件進行文件對象實例化,文件名,文件大小,代碼行數
        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)

優化後,獲得咱們須要的結果:

 

(完) 

相關文章
相關標籤/搜索