mik*_*ood 2 delphi zlib delphi-10.1-berlin
我在 Delphi 中使用 ZLib 单元解压时遇到了一个小问题
unit uZCompression;
interface
uses
uCompression;
type
TZZipCompression = class(TInterfacedObject, ICompression)
public
function DoCompression(aContent: TArray<Byte>): TArray<Byte>;
function DoDecompression(aContent: TArray<Byte>): TArray<Byte>;
function GetWindowsBits: Integer; virtual;
end;
TZGZipCompression = class(TZZipCompression)
function GetWindowsBits: Integer; override;
end;
implementation
uses
System.ZLib, System.Classes, uMxKxUtils;
{ TZCompression }
function TZZipCompression.DoCompression(aContent: TArray<Byte>): TArray<Byte>;
var
LContentStream, LOutputStream: TMemoryStream;
LCompressedStream: TZCompressionStream;
begin
LContentStream := ByteArrayToStream(aContent);
LOutputStream := TMemoryStream.Create;
LCompressedStream := TZCompressionStream.Create(LOutputStream, zcDefault, GetWindowsBits);
LCompressedStream.CopyFrom(LContentStream, LContentStream.Size);
LCompressedStream.Free;
Result := StreamToByteArray(LOutputStream);
LOutputStream.Free;
LContentStream.Free;
end;
function TZZipCompression.DoDecompression(aContent: TArray<Byte>): TArray<Byte>;
var
LContentStream, LOutputStream: TMemoryStream;
LDecompressedStream: TZDecompressionStream;
begin
LContentStream := ByteArrayToStream(aContent);
LOutputStream := TMemoryStream.Create;
LDecompressedStream := TZDecompressionStream.Create(LContentStream);
LOutputStream.CopyFrom(LDecompressedStream, LDecompressedStream.Size);
LDecompressedStream.Free;
Result := StreamToByteArray(LOutputStream);
LOutputStream.Free;
LContentStream.Free;
end;
function TZZipCompression.GetWindowsBits: Integer;
begin
Result := 15;
end;
{ TZGZipCompression }
function TZGZipCompression.GetWindowsBits: Integer;
begin
Result := inherited;
Result := Result + 16;
end;
end.
Run Code Online (Sandbox Code Playgroud)
这是我的单元,由接口驱动(您不需要知道),数据通过 TArray 变量传入和传出。
我已经将它编码为能够进行两种类型的压缩,标准 zip 和 gzip,这是由传递给函数的 windowsbits 决定的。
以下是用于将 TArray 转换为 TMemoryStream 的其他几个函数
function ByteArrayToStream(aContent: TArray<Byte>): TMemoryStream;
begin
Result := TMemoryStream.Create;
Result.Write(aContent, length(aContent)*SizeOf(aContent[0]));
Result.Position := 0;
end;
function StreamToByteArray(aStream: TMemoryStream): TArray<Byte>;
var
LStreamPos: Int64;
begin
if Assigned(aStream) then
begin
LStreamPos := aStream.Position;
aStream.Position := 0;
SetLength(Result, aStream.Size);
aStream.Read(Result, aStream.Size);
aStream.Position := LStreamPos;
end
else
SetLength(Result, 0);
end;
Run Code Online (Sandbox Code Playgroud)
现在我可以使用 TZZipCompression 类完美地压缩和解压缩到 .zip(它不会作为 zip 文件打开,但它确实解压缩回我可以打开和编辑的原始文件)。
我也可以使用 TZGZipCompression 类很好地压缩到 .gz(有趣的是我可以很好地打开这个 gzip 文件)。
然而,我的问题是它不会从 .gz 文件解压回来,并且一旦命中就会抛出和错误
LOutputStream.CopyFrom(LDecompressedStream, LDecompressedStream.Size)
Run Code Online (Sandbox Code Playgroud)
有趣的是,帮助文件示例如下
LOutputStream.CopyFrom(LDecompressedStream, 0)
Run Code Online (Sandbox Code Playgroud)
但这也行不通。
任何人都可以发现问题吗?
您在TArray<Byte>和之间的转换函数TMemoryStream是错误的,因为您没有正确访问数组内容。 TArray是一个动态数组。调用TMemoryStream.Write()and 时TMemoryStream.Read(),传递的是TArray自身的内存地址,而不是TArray指向的数据的内存地址。您需要引用TArray以获取正确的内存地址,例如:
function ByteArrayToStream(const aContent: TArray<Byte>): TMemoryStream;
begin
Result := TMemoryStream.Create;
try
if Length(aContent) > 0 then
Result.WriteBuffer(aContent[0], Length(aContent));
Result.Position := 0;
except
Result.Free;
raise;
end;
end;
function StreamToByteArray(aStream: TMemoryStream): TArray<Byte>;
begin
if Assigned(aStream) then
begin
SetLength(Result, aStream.Size);
if Length(Result) > 0 then
Move(aStream.Memory^, Result[0], aStream.Size);
end
else
SetLength(Result, 0);
end;
Run Code Online (Sandbox Code Playgroud)
或者:
function ByteArrayToStream(const aContent: TArray<Byte>): TMemoryStream;
begin
Result := TMemoryStream.Create;
try
Result.WriteBuffer(PByte(aContent)^, Length(aContent));
Result.Position := 0;
except
Result.Free;
raise;
end;
end;
function StreamToByteArray(aStream: TMemoryStream): TArray<Byte>;
begin
if Assigned(aStream) then
begin
SetLength(Result, aStream.Size);
Move(aStream.Memory^, PByte(Result)^, aStream.Size);
end
else
SetLength(Result, 0);
end;
Run Code Online (Sandbox Code Playgroud)
话虽如此,您无需浪费内存使用TMemoryStream. 您可以TBytesStream改用(因为动态数组是引用计数的),例如:
function TZZipCompression.DoCompression(aContent: TArray<Byte>): TArray<Byte>;
var
LContentStream, LOutputStream: TBytesStream;
LCompressedStream: TZCompressionStream;
begin
LContentStream := TBytesStream.Create(aContent);
try
LOutputStream := TBytesStream.Create(nil);
try
LCompressedStream := TZCompressionStream.Create(LOutputStream, zcDefault, GetWindowsBits);
try
LCompressedStream.CopyFrom(LContentStream, 0);
finally
LCompressedStream.Free;
end;
Result := Copy(LOutputStream.Bytes, 0, LOutputStream.Size);
finally
LOutputStream.Free;
end;
finally
LContentStream.Free;
end;
end;
function TZZipCompression.DoDecompression(aContent: TArray<Byte>): TArray<Byte>;
var
LContentStream, LOutputStream: TBytesStream;
LDecompressedStream: TZDecompressionStream;
begin
LContentStream := TBytesStream.Create(aContent);
try
LOutputStream := TBytesStream.Create(nil);
try
LDecompressedStream := TZDecompressionStream.Create(LContentStream, GetWindowsBits);
try
LOutputStream.CopyFrom(LDecompressedStream, 0);
finally
LDecompressedStream.Free;
end;
Result := Copy(LOutputStream.Bytes, 0, LOutputStream.Size);
finally
LOutputStream.Free;
end;
finally
LContentStream.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)