下面的代码在Win32上工作,无论如何,如果在Android或iOS上运行,它会出现异常。例外情况是:"在目标多字节代码页中不存在Unicode字符的映射"
function Form1.ZDecompressString(aText: String): String;
var
strInput,
strOutput : TStringStream;
Unzipper : TZDecompressionStream;
begin
Result := '';
strInput := TStringStream.Create( aText );
strOutput := TStringStream.Create;
try
Unzipper := TZDecompressionStream.Create( strInput );
try
strOutput.CopyFrom( Unzipper, Unzipper.Size );
finally
Unzipper.Free;
end;
Result := strOutput.DataString;
finally
strInput.Free;
strOutput.Free;
end;
end;
procedure Form1.getUsers;
var
XMLRqst : String;
XMLResponse : TStringStream;
XMLRequest : TStringStream;
idHTTP : TIdHTTP;
s : String;
begin
XMLRqst := UTF8ToString( '<root company="belvew"/>' );
XMLRequest := TStringStream.Create( XMLRqst, TEncoding.UTF8 );
XMLResponse := TStringStream.Create( '' );
try
try
idHTTP := TIdHTTP.Create( Self );
idHTTP.CookieManager := idCookieManager;
idHTTP.ReadTimeout := 60000;{ IdTimeoutInfinite; }
idHTTP.ConnectTimeout := 60000;
idHTTP.HandleRedirects := True;
XMLResponse.Position := 0;
XMLRequest.Position := 0;
idHTTP.Post( 'http://localhost/API/getUsers.aspx', XMLRequest, XMLResponse );
idHTTP.Disconnect;
unique_id := ZDecompressString( XMLResponse.DataString );
XMLRequest.Free;
XMLResponse.Free;
except
on E : Exception do
begin
ShowMessage( 'exception : '#13 + E.Message );
end;
end;
finally
idHTTP.Free;
end;
end;
procedure Form1.onCreate( Sender : TObject );
begin
getUsers;
end;
- 为什么代码不能在Android或iOS下工作?
我想说您需要扔掉这段代码并重新开始。像我遇到的所有压缩算法一样,zlib不对文本进行操作。它对字节数组进行操作。在Delphi字符串使用8位编码的时候,人们对这样的字符串玩得很随意,把它们当作字节数组来对待。从那时起,压缩可以作用于字符串的误解就一直存在。
您需要选择一种编码来将文本转换为字节数组。一个不错的选择是UTF-8。然后,如果您想将压缩数据表示为文本,则需要使用base64之类的编码。
压缩过程如下:
- 将文本编码为UTF-8字节数组
- 用base64编码压缩字节数组
在相反的方向,你会:
- 解码base64文本到压缩字节数组
- 解码UTF-8编码的字节数组为文本
编码/解码UTF-8使用TEncoding.UTF8
。GetBytes
和GetString
方法是您所需要的。对于base64编码/解码,最好建议您使用Indy库,因为您已经在使用它了。
根据注释,您已经解压缩到UTF-8编码的字节数组。在这种情况下,最后一步是写入:
text := TEncoding.UTF8.GetString(utf8byteArray);
除了David所说的,您的代码对其中一些使用未指定的编码进行了太多的字节<->字符串转换,因此您受到可能丢失数据的ANSI<->Unicode转换的影响。
更重要的是,您真正要做的是解压缩HTTP响应的原始二进制数据,但是使用TStringStream
来完成该操作。这本身就是错误的,因为压缩操作的是字节,而不是字符。而且,TIdHTTP
具有内置支持用于解压缩HTTP响应,如果响应具有deflate
或gzip
的Content-Encoding
头。在本例中,由于没有填写TIdHTTP.Request.AcceptEncoding
属性,因此不应该有http级别的压缩。但是让我们假设存在(例如,错误的服务器实现)。要启用该功能,您所要做的就是分配TIdHTTP.Compressor
属性,然后让TIdHTTP
在内部为您完成其余的工作。这将大大简化代码,例如:
uses
..., IdCompressorZLib;
procedure Form1.getUsers;
var
XMLRqst : String;
XMLRequest : TStringStream;
idHTTP : TIdHTTP;
begin
XMLRqst := '<root company="belvew"/>';
try
XMLRequest := TStringStream.Create( XMLRqst, TEncoding.UTF8 );
try
idHTTP := TIdHTTP.Create;
try
idHTTP.CookieManager := idCookieManager;
idHTTP.ReadTimeout := 60000;{ IdTimeoutInfinite; }
idHTTP.ConnectTimeout := 60000;
idHTTP.HandleRedirects := True;
idHTTP.Compressor := TIdCompressorZLib.Create(idHTTP);
unique_id := idHTTP.Post( 'http://localhost/API/getUsers.aspx', XMLRequest );
finally
idHTTP.Free;
end;
finally
XMLRequest.Free;
end;
except
on E : Exception do
begin
ShowMessage( 'exception : '#13 + E.Message );
end;
end;
end;