You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:09:58