选择性下载和提取数据(CAB)

时间:2018-09-06 16:10:25

标签: python-3.x archive compression

因此,我特别需要下载和解压缩cab文件,但是每个cab文件的大小都大于200MB。我想有选择地从出租车上下载文件,因为其余数据无用。

到目前为止已经完成很多事情了:

  1. 从服务器请求文件的1%。获取标题并解析它们。
  2. 获取文件列表,其偏移量根据This CAB Link
  3. 将“ Range”标头设置为文件“ Offset”和“ Offset + Size”发送GET请求到服务器。
  4. 我能够获得响应,但由于它被压缩(LZX:21-Acc to 7Zip),因此它以“无法读取”的方式提供
  5. 无法使用zlib解压缩。抛出无效标题。

我也不十分了解,也无法跟踪示例中所示的 CFFOLDER CFDATA 导致其未压缩。

totalByteArray =b''
eofiles =0
def GetCabMetaData(stream):
    global eofiles
    cabMetaData={}
    try:
        cabMetaData["CabFormat"] = stream[0:4].decode('ANSI')
        cabMetaData["CabSize"] = struct.unpack("<L",stream[8:12])[0]
        cabMetaData["FilesOffset"] = struct.unpack("<L",stream[16:20])[0]
        cabMetaData["NoOfFolders"] = struct.unpack("<H",stream[26:28])[0]
        cabMetaData["NoOfFiles"] = struct.unpack("<H",stream[28:30])[0]
        # skip 30,32,34,35
        cabMetaData["Files"]= {}
        cabMetaData["Folders"]= {}
        baseOffset = cabMetaData["FilesOffset"]
        internalOffset = 0
        for i in range(0,cabMetaData["NoOfFiles"]):
            fileDetails = {}
            fileDetails["Size"] =  struct.unpack("<L",stream[baseOffset+internalOffset:][:4])[0]
            fileDetails["UnpackedStartOffset"] = struct.unpack("<L",stream[baseOffset+internalOffset+4:][:4])[0]
            fileDetails["FolderIndex"] = struct.unpack("<H",stream[baseOffset+internalOffset+8:][:2])[0]
            fileDetails["Date"] = struct.unpack("<H",stream[baseOffset+internalOffset+10:][:2])[0]
            fileDetails["Time"] = struct.unpack("<H",stream[baseOffset+internalOffset+12:][:2])[0]
            fileDetails["Attrib"] = struct.unpack("<H",stream[baseOffset+internalOffset+14:][:2])[0]
            fileName =''
            for j in range(0,len(stream)):
                if(chr(stream[baseOffset+internalOffset+16 +j])!='\x00'):
                    fileName +=chr(stream[baseOffset+internalOffset+16 +j])
                else:
                    break
            internalOffset += 16+j+1
            cabMetaData["Files"][fileName] = (fileDetails.copy())
        eofiles = baseOffset + internalOffset

    except Exception as e:
        print(e)
        pass
    print(cabMetaData["CabSize"])
    return cabMetaData

def GetFileSize(url):
    resp = requests.head(url)
    return int(resp.headers["Content-Length"])

def GetCABHeader(url):
    global totalByteArray
    size = GetFileSize(url)
    newSize ="bytes=0-"+ str(int(0.01*size))
    totalByteArray = b''
    cabHeader= requests.get(url,headers={"Range":newSize},stream=True)
    for chunk in cabHeader.iter_content(chunk_size=1024):
        totalByteArray += chunk


def DownloadInfFile(baseUrl,InfFileData,InfFileName):

    global totalByteArray,eofiles
    if(not os.path.exists("infs")):
        os.mkdir("infs")
    baseCabName = baseUrl[baseUrl.rfind("/"):]
    baseCabName = baseCabName.replace(".","_")
    if(not os.path.exists("infs\\" + baseCabName)):
        os.mkdir("infs\\"+baseCabName)
    fileBytes = b''
    newRange = "bytes=" + str(eofiles+InfFileData["UnpackedStartOffset"] ) + "-" + str(eofiles+InfFileData["UnpackedStartOffset"]+InfFileData["Size"] )
    data = requests.get(baseUrl,headers={"Range":newRange},stream=True)
    with open("infs\\"+baseCabName +"\\" + InfFileName  ,"wb") as f:
        for chunk in data.iter_content(chunk_size=1024):
            fileBytes +=chunk
        f.write(fileBytes)
        f.flush()


    print("Saved File " + InfFileName)
    pass


def main(url):
    GetCABHeader(url)
    cabMetaData = GetCabMetaData(totalByteArray)
    for fileName,data in cabMetaData["Files"].items():
        if(fileName.endswith(".txt")):
            DownloadInfFile(url,data,fileName)

main("http://path-to-some-cabinet.cab")

所有文件详细信息正确。我已经验证了他们。

任何指导将不胜感激。我做错了吗?也许是另一种方式?

P.S:已经调查过This Post

1 个答案:

答案 0 :(得分:0)

首先,CAB中的数据是原始放气,而不是zlib包装的放气。因此,您需要让zlib的inflate()在初始化时使用负windowBits的值对原始放气进行解码。

第二,CAB格式不完全使用标准deflate,因为32K滑动窗口字典从一个块到下一个块。您需要使用inflateSetDictionary()在每个块的开始处使用从最后一个块解压缩的最后32K来设置字典。