使用pycurl解压lz4压缩OS镜像写入磁盘时分区调整报错求助
问题:pycurl流式解压LZ4镜像写入磁盘导致分区表损坏
使用pycurl获取OS镜像并流式解压写入磁盘时,GZIP格式处理正常,但LZ4格式写入后调整分区大小出现以下错误:
条目大小为0字节,但本程序仅支持128字节的条目。
已相应调整,但分区表可能已损坏。
警告:分区表表头声明分区表条目大小为0字节,但本程序仅支持128字节的条目。
已相应调整,但分区表可能已损坏。
正在内存中创建新的GPT条目。
操作已成功完成。
错误:分区不存在
通过io.BytesIO全量缓存后解压再写入的方式可以正常工作,代码如下:
if url.endswith('.lz4'): with io.BytesIO() as output_buffer: curl.setopt(pycurl.WRITEDATA, output_buffer) curl.perform() output_buffer.seek(0) decompressed_data = lz4.frame.decompress(output_buffer.read()) disk.write(decompressed_data)
但希望实现流式处理避免全量缓存,尝试的代码未成功:
def write_to_disk(self, url, dev, proxy=None): if os.path.isfile(dev): size = os.path.getsize(dev) with open(os.path.realpath(dev), 'wb') as disk: disk.seek(0) def write_gz(buf): disk.write(d.decompress(buf)) def write_lz4(buf): disk.write(lz4.decompress(buf)) try: curl = pycurl.Curl() curl.setopt(pycurl.URL, url) if proxy is not False: curl.setopt(pycurl.PROXY, proxy) curl.setopt(pycurl.BUFFERSIZE, 1024) if url.endswith('.lz4'): curl.setopt(pycurl.WRITEFUNCTION, write_lz4) elif url.endswith('.gz'): d = zlib.decompressobj(zlib.MAX_WBITS | 32) curl.setopt(pycurl.WRITEFUNCTION, write_gz) curl.perform() except pycurl.error: return False if os.path.isfile(dev): disk.seek(size - 1) disk.write(b"\0") return True
解决方案
问题出在LZ4的解压方式:你用的lz4.decompress()是处理原始LZ4块的,而OS镜像采用的是LZ4 frame格式(对应lz4.frame模块的处理逻辑)。流式处理时需要用lz4.frame.Decompressor()来逐块处理数据,而不是直接用lz4.decompress()。
修改后的代码如下:
def write_to_disk(self, url, dev, proxy=None): if os.path.isfile(dev): size = os.path.getsize(dev) with open(os.path.realpath(dev), 'wb') as disk: disk.seek(0) def write_gz(buf): disk.write(d.decompress(buf)) # 初始化LZ4 frame流式解压器 lz4_decompressor = lz4.frame.Decompressor() def write_lz4(buf): # 流式解压当前块并写入磁盘 disk.write(lz4_decompressor.decompress(buf)) try: curl = pycurl.Curl() curl.setopt(pycurl.URL, url) if proxy is not False: curl.setopt(pycurl.PROXY, proxy) curl.setopt(pycurl.BUFFERSIZE, 1024) if url.endswith('.lz4'): curl.setopt(pycurl.WRITEFUNCTION, write_lz4) elif url.endswith('.gz'): d = zlib.decompressobj(zlib.MAX_WBITS | 32) curl.setopt(pycurl.WRITEFUNCTION, write_gz) curl.perform() # 处理LZ4解压的剩余数据 if url.endswith('.lz4'): remaining_data = lz4_decompressor.flush() if remaining_data: disk.write(remaining_data) except pycurl.error: return False if os.path.isfile(dev): disk.seek(size - 1) disk.write(b"\0") return True
关键修改点
- 初始化
lz4.frame.Decompressor()实例,用于流式处理LZ4 frame数据 write_lz4函数用该实例的decompress()方法处理每一块缓存数据- 在
curl.perform()完成后,调用flush()获取剩余的解压数据并写入磁盘
这样就能实现和io.BytesIO全量处理一致的正确解压效果,同时避免全量缓存的冗余。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

