求助:将TByteDynArray转字符串/TMemoryStream后PDF保存损坏,哪里出错?
我需要把TByteDynArray类型变量的内容复制到字符串变量,或者更理想的是复制到TMemoryStream中。之前参考过Remy的解答,但实际操作时遇到问题:
obtReferenciaPagamentoResponse.pdf是通过WebService调用获取的TByteDynArray类型(XSD中定义为xsd:base64Binary),我写了下面的saveFile过程尝试保存为PDF文件,但保存后的文件始终损坏无法打开:
procedure saveFile; var LInput, LOutput: TMemoryStream; Id: Integer; Buff: AnsiString; //Buff: String; begin LInput := TMemoryStream.Create; LOutput := TMemoryStream.Create; // Tried like this also //SetLength(Buff, Length(obtReferenciaPagamentoResponse.pdf)); //Move(obtReferenciaPagamentoResponse.pdf[0], Buff[1], Length(obtReferenciaPagamentoResponse.pdf)); // Tried other charsets Buff := TEncoding.Ansi.GetString(obtReferenciaPagamentoResponse.pdf); LInput.Write(Buff[1], Length(Buff) * SizeOf(Buff[1])); LInput.Position := 0; TNetEncoding.Base64.Decode(LInput, LOutput); LOutput.Position := 0; LOutput.SaveToFile(SaveDialog2.FileName); LInput.Free; LOutput.Free; end;
请问我哪里操作出错了?
你的代码有两个关键错误:
多做了一次Base64解码
WebService返回的TByteDynArray对应xsd:base64Binary时,Delphi的SOAP框架已经自动完成了Base64解码,这个数组里的内容就是PDF的原始二进制数据。你再调用TNetEncoding.Base64.Decode,相当于把已经是原始数据的内容又做了一次Base64解码,直接破坏了文件结构。用字符串中转二进制数据错误
二进制数据(比如PDF)不能直接用AnsiString或String中转,TEncoding.Ansi.GetString会把字节按ANSI编码规则转换成字符,这个过程会改变原始字节值,导致数据损坏。哪怕用Move直接拷贝字节到字符串,字符串的管理逻辑也可能对二进制数据造成干扰。
修正后的代码(直接保存到文件)
如果只是要保存成PDF文件,不需要用MemoryStream中转,直接操作TByteDynArray即可:
procedure saveFile; var LStream: TMemoryStream; begin LStream := TMemoryStream.Create; try // 直接把TByteDynArray的二进制数据写入流 if Length(obtReferenciaPagamentoResponse.pdf) > 0 then LStream.WriteBuffer(obtReferenciaPagamentoResponse.pdf[0], Length(obtReferenciaPagamentoResponse.pdf)); LStream.SaveToFile(SaveDialog2.FileName); finally LStream.Free; end; end;
若需要转存到TMemoryStream
如果确实需要把TByteDynArray复制到TMemoryStream,代码可以更简洁:
procedure CopyByteDynArrayToStream(const AArray: TByteDynArray; AStream: TStream); begin if (AStream <> nil) and (Length(AArray) > 0) then AStream.WriteBuffer(AArray[0], Length(AArray)); end; // 调用示例: var LStream: TMemoryStream; begin LStream := TMemoryStream.Create; try CopyByteDynArrayToStream(obtReferenciaPagamentoResponse.pdf, LStream); LStream.Position := 0; // 这里可以对LStream做后续操作,比如保存文件、传输等 LStream.SaveToFile(SaveDialog2.FileName); finally LStream.Free; end; end;
内容的提问来源于stack exchange,提问作者Carlos Matos

