本人在使用Delphi TIdFtp进行压缩方式下载时,发现有时会报一个EIdReplyRFCError类型的错误,错误信息为Data connection closed abnormally。经分析发现是由于IdCompressorZLib单元的procedure TIdCompressorZLib.InternalDecompressStream处理不当造成的。估计全部Delphi版本自带的TIdFtp估计都有此问题。 本人在使用Delphi TIdFtp进行压缩方式下载时,发现有时会报一个EIdReplyRFCError类型的错误,错误信息为Data connection closed abnormally。经分析发现是由于IdCompressorZLib单元的procedure TIdCompressorZLib.InternalDecompressStream处理不当造成的。估计全部Delphi版本自带的TIdFtp估计都有此问题。解决方法如下:
在procedure TIdCompressorZLib.InternalDecompressStream中找到
DCheck(inflate(LZstream,Z_NO_FLUSH)); //把这行改为下命两行:
zresult := inflate(LZstream, Z_NO_FLUSH);
if zresult <> Z_BUF_ERROR then DCheck(zresult); //Z_BUF_ERROR属于正常情况
另外,从Delphi 10.3 Rio开始,由于Delphi自带的zlib版本更新,需要做如下改动:
procedure InternalDecompressStream(LZstream: TZStreamRec; AIOHandler : TIdIOHandler;
AOutStream: TStream);
改为
procedure InternalDecompressStream(var LZstream: TZStreamRec; AIOHandler : TIdIOHandler;
AOutStream: TStream);
也就是把LZstream从传值改为传地址
----------------------------------------------------------------------------------------------------------------------------
更详细的解决方法请参考下面的内容(以Delphi 11.2为例):
procedure TIdCompressorZLib.InternalDecompressStream(
var LZstream: TZStreamRec; AIOHandler: TIdIOHandler; AOutStream: TStream);
var
zresult : Integer;
outBuffer: Array [0..bufferSize-1] of TIdAnsiChar;
inSize : Integer;
outSize : Integer;
LBuf : TIdBytes;
function RawReadFromIOHandler(ABuffer : TIdBytes; AOIHandler : TIdIOHandler; AMax : Integer) : Integer;
begin
try
AIOHandler.ReadBytes(ABuffer, AMax, False);
except
on E: Exception do begin
AMax := IndyMin(AMax, AIOHandler.InputBuffer.Size);
AIOHandler.InputBuffer.ExtractToBytes(ABuffer, AMax, False);
if not (E is EIdConnClosedGracefully) then begin
raise;
end;
end;
end;
TIdAntiFreezeBase.DoProcess;
Result := AMax;
end;
begin
SetLength(LBuf, bufferSize);
repeat
inSize := RawReadFromIOHandler(LBuf, AIOHandler, bufferSize);
if inSize < 1 then begin
Break;
end;
LZstream.next_in := PIdAnsiChar(@LBuf[0]);
LZstream.avail_in := inSize;
repeat
LZstream.next_out := @outBuffer[0];
LZstream.avail_out := bufferSize;
//DCheck(inflate(LZstream,Z_NO_FLUSH)); //<---此行处理不当,改为下面两行: <-------------------------------------
zresult := inflate(LZstream, Z_NO_FLUSH);
if zresult <> Z_BUF_ERROR then DCheck(zresult); //Z_BUF_ERROR属于正常情况
//--------------------------------------------------------------------------
outSize := bufferSize - LZstream.avail_out;
AOutStream.Write(outBuffer, outSize);
until (LZstream.avail_in = 0) and (LZstream.avail_out > 0);
until False;
repeat
LZstream.next_out := @outBuffer[0];
LZstream.avail_out := bufferSize;
zresult := inflate(LZstream, Z_FINISH);
if zresult <> Z_BUF_ERROR then
begin
zresult := DCheck(zresult);
end;
outSize := bufferSize - LZstream.avail_out;
AOutStream.Write(outBuffer, outSize);
until ((zresult = Z_STREAM_END) and (LZstream.avail_out > 0)) or (zresult = Z_BUF_ERROR);
DCheck(inflateEnd(LZstream));
end;