LZO压缩:如何设置目标输出长度?
解决Delphi调用LZO.dll压缩时的OutputLength设置与实际压缩数据写入问题
嘿,我之前在Delphi里调用LZO.dll压缩文件时也踩过这个OutputLength的坑,给你梳理下核心思路和可直接用的代码示例:
核心原理:OutputLength是「输入输出参数」
LZO的压缩函数(比如常用的lzo1x_1_compress)里,OutputLength(对应参数里的dst_len)是双向的:
- 调用前:你需要传入你分配的输出缓冲区的最大可用大小(必须足够容纳压缩后的数据,LZO有官方推荐的计算方式)
- 调用后:这个参数会被LZO更新为实际压缩后的数据长度——这正是你需要写入文件的大小!
具体步骤与代码实现
首先确保你正确声明了LZO的函数和常量(Delphi里的DLL导入):
type lzo_uint = Cardinal; const LZO_E_OK = 0; LZO1X_1_MEM_COMPRESS = 16384; // LZO1X_1_COMPRESS需要的工作内存大小 function lzo_init: Integer; stdcall; external 'lzo.dll'; function lzo1x_1_compress(const src: PByte; src_len: lzo_uint; dst: PByte; var dst_len: lzo_uint; wrkmem: PByte): Integer; stdcall; external 'lzo.dll';
然后是完整的压缩文件函数,关键部分我做了注释:
procedure CompressFileWithLZO(const SourceFile, DestFile: string); var hIn, hOut: THandle; srcBuf, dstBuf, wrkBuf: PByte; srcSize, dstSize, wrkSize: lzo_uint; bytesRead: DWORD; res: Integer; begin // 第一步:必须初始化LZO,否则压缩会失败 res := lzo_init(); if res <> LZO_E_OK then raise Exception.CreateFmt('LZO初始化失败,错误码: %d', [res]); // 分配LZO压缩需要的工作内存 wrkSize := LZO1X_1_MEM_COMPRESS; wrkBuf := GetMem(wrkSize); try // 打开并读取源文件到内存 hIn := CreateFile(PChar(SourceFile), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if hIn = INVALID_HANDLE_VALUE then raise Exception.Create('无法打开源文件: ' + SourceFile); try srcSize := GetFileSize(hIn, nil); srcBuf := GetMem(srcSize); try if not ReadFile(hIn, srcBuf^, srcSize, bytesRead, nil) or (bytesRead <> srcSize) then raise Exception.Create('读取源文件内容失败'); // 计算输出缓冲区的最小安全大小(LZO官方推荐公式) dstSize := srcSize + srcSize div 16 + 64 + 3; // 也可以用更保守的srcSize * 2,确保绝对足够(牺牲一点内存,换稳定性) // dstSize := srcSize * 2; dstBuf := GetMem(dstSize); try // 调用压缩函数:此时dstSize是缓冲区大小,调用后会被更新为实际压缩长度 res := lzo1x_1_compress(srcBuf, srcSize, dstBuf, dstSize, wrkBuf); if res <> LZO_E_OK then raise Exception.CreateFmt('压缩失败,错误码: %d', [res]); // 重点:只写入实际压缩后的dstSize字节,而不是整个缓冲区 hOut := CreateFile(PChar(DestFile), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if hOut = INVALID_HANDLE_VALUE then raise Exception.Create('无法创建目标文件: ' + DestFile); try if not WriteFile(hOut, dstBuf^, dstSize, bytesRead, nil) or (bytesRead <> dstSize) then raise Exception.Create('写入压缩数据到文件失败'); finally CloseHandle(hOut); end; finally FreeMem(dstBuf); end; finally FreeMem(srcBuf); end; finally CloseHandle(hIn); end; finally FreeMem(wrkBuf); end; end;
额外注意事项
- 如果遇到压缩后数据比原数据还大的情况(比如原文件已经是压缩过的),LZO会返回
LZO_E_OK,但dstSize会大于srcSize。这时候你可以加个判断,直接写入原数据而不是压缩后的内容,避免文件变大。 - 不同的LZO压缩算法(比如
lzo1x_999)需要的工作内存大小不同,要对应调整wrkSize的取值(比如LZO1X_999_MEM_COMPRESS)。
内容的提问来源于stack exchange,提问作者Delutto
相关产品推荐
相关产品推荐

