Python3實現zip分卷壓縮過程解析
使用zipfile庫
檢視 官方中文文件
利用 Python 壓縮 ZIP 檔案,我們第一反應是使用 zipfile 庫,然而,它的官方文件中卻明確標註“此模組目前不能處理分卷 ZIP 檔案”,(⊙﹏⊙)
折騰經過
翻遍了Google、CSDN、Stackoverflow等平臺均未找到解決方案,最靠譜的是呼叫外部解壓程式實現分卷壓縮的功能。但是,如何不依靠外部程式實現這個功能呢??
於是乎,只能自己慢慢造輪子。看著 ZIP 格式開發商留下的文件 ZIP File Format Specification,頭疼啊(;´д`)。於是我拿著 WinHex 開始16進位制一個一個檔案對比 WinRar 建立的分卷壓縮和單個 zip 檔案的差異。最後還真的整出來了( ̄▽ ̄)"
如果想把單個大檔案 test.zip -> 分卷檔案 test.z01、test.z02、test.zip
首先,在建立的第一個分卷檔案 test.z01的前面加上 \x50\x4b\x07\x08 這個是分卷壓縮的檔案頭(header),佔4個位元組。其實單個壓縮檔案本身 header 就有這個了,而分卷壓縮的需要兩個emmm。之後便是從單個大壓縮檔案檔案test.zip中讀取 "一個分卷大小 -4 個位元組"的資料,寫入test.z01中,如何接著讀取一個分卷大小的資料,寫入test.z02,以此類推,最後一個分卷檔名也是test.zip。
Python3的程式碼實現
import os import zipfile def zip_by_volume(file_path,block_size): """zip檔案分卷壓縮""" file_size = os.path.getsize(file_path) # 檔案位元組數 path,file_name = os.path.split(file_path) # 除去檔名以外的path,檔名 suffix = file_name.split('.')[-1] # 檔案字尾名 # 新增到臨時壓縮檔案 zip_file = file_path + '.zip' with zipfile.ZipFile(zip_file,'w') as zf: zf.write(file_path,arcname=file_name) # 小於分捲尺寸則直接返回壓縮檔案路徑 if file_size <= block_size: return zip_file else: fp = open(zip_file,'rb') count = file_size // block_size + 1 # 建立分卷壓縮檔案的儲存路徑 save_dir = path + os.sep + file_name + '_split' if os.path.exists(save_dir): from shutil import rmtree rmtree(save_dir) os.mkdir(save_dir) # 拆分壓縮包為分卷檔案 for i in range(1,count + 1): _suffix = 'z{:0>2}'.format(i) if i != count else 'zip' name = save_dir + os.sep + file_name.replace(str(suffix),_suffix) f = open(name,'wb+') if i == 1: f.write(b'\x50\x4b\x07\x08') # 新增分卷壓縮header(4位元組) f.write(fp.read(block_size - 4)) else: f.write(fp.read(block_size)) fp.close() os.remove(zip_file) # 刪除臨時的 zip 檔案 return save_dir if __name__ == '__main__': file = r"D:\Downloads\1.mp4" # 原始檔案 volume_size = 1024 * 1024 * 100 # 分卷大小 100MB path = zip_by_volume(file,volume_size) print(path) # 輸出分卷壓縮檔案的路徑
缺點
該方法建立分卷壓縮的時候,需要先在磁碟建立一個臨時壓縮包,然後將其拆分,實際上會對磁碟寫入兩次,這就浪費了時間。
當然,我嘗試使用 ByteIO 進行位元組流的壓縮,但是這種方式需要先把檔案讀入記憶體,對於超級大的檔案,這是不現實的,分分鐘記憶體爆炸。
然後,我嘗試使用 io.pipe 的管道來處理,而 zipfile 壓縮需要提供一個 file 或 file-like 物件,這個物件必須實現 seek() 和 tell() 方法來回去寫入檔案頭資訊,然而管道流沒辦法seek回去修改資料。這裡,參考了Python zipfile + os.pipe()探索記,遮蔽了 seek() 和 tell() 函式。但是,後面我分卷時需要指定讀取的位元組數,這就需要這兩個函式。。。我大概知道為什麼 zipfile 庫不支援建立分卷檔案了〒▽〒
這個庫的作者也沒少掉頭髮。。。現在就將就一下,這樣用著吧。。。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。