1. 程式人生 > 程式設計 >Python3實現zip分卷壓縮過程解析

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 庫不支援建立分卷檔案了〒▽〒

這個庫的作者也沒少掉頭髮。。。現在就將就一下,這樣用著吧。。。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。