Search code examples
delphidelphi-xe8

Reading the data string From TStringStream


I have an issue with TStringStream encoding on different OS region languages.

I am trying to transfer image data. I was having a problem with it before, and it was solved in another question.

But now I have an encoding issue on TStringStream itself. If the Operating System region language is set to English, I get corrupted data from TStringStream.

When I change the language to Arabic, the data comes out correctly.

I currently use Indy to encode the binary data, but before starting the encoding, the data from StringStream.DataString is already corrupted if the region encoding is not set to Arabic. I tried to add TEncoding.UTF8 to TStringStream.Create(), but the data still comes out incorrectly from StringStream.DataString.

function Encode64(const S: string; const ByteEncoding: IIdTextEncoding = nil): string;
begin
  Result := TIdEncoderMIME.EncodeString(S, ByteEncoding);
end;

function Decode64(const S: string; const ByteEncoding: IIdTextEncoding = nil): string;
begin
  Result := TIdDecoderMIME.DecodeString(S, ByteEncoding);
end;

StringStream := TStringStream.Create('');
try
  Jpg.Performance := jpBestSpeed;
  Jpg.ProgressiveEncoding := True;
  Jpg.ProgressiveDisplay := True;
  jpg.Assign(Image2.Picture.Bitmap);
  jpg.CompressionQuality := 25;
  jpg.Compress;
  jpg.SaveToStream(StringStream);
  StringImageData := StringStream.DataString; // the data here is corrupted when Os region is not set to arabic 
  strcams := '<[S:' + IntToStr(Length(StringImageData)) + 'B]>' + StringImageData;
  if length(strcams) < byt then begin
    Sendimgdata('IMGDATA123', Encode64(strcams, IndyTextEncoding_UTF8) + sep);
  end;
  ...

Solution

  • You cannot save a JPG to a TStringStream to begin with. You need to encode the binary data without converting it to a string first, which will corrupt the data. Use a TMemoryStream instead for the binary data:

    MemoryStream := TMemoryStream.Create;
    ...
    jpg.SaveToStream(MemoryStream);
    MemoryStream.Position := 0;
    StringImageData := TIdEncoderMIME.EncodeStream(MemoryStream);
    ...
    

    If you want to encode after you insert the text with length, you have to work with TMemoryStream too:

    procedure StringToStream(aStream: TStream; const aString: AnsiString);
    begin
      aStream.Write(PAnsiChar(AString)^, Length(AString));
    end;
    
    ...
    
    JpegStream := TMemoryStream.Create;
    jpg.SaveToStream(JpegStream);
    
    CompleteStream := TMemoryStream.Create;
    StringToStream(CompleteStream, '<[S:' + IntToStr(JpegStream.Size)+'B]>');
    
    CompleteStream.CopyFrom(JpegStream, 0);
    StringImageData := TIdEncoderMIME.EncodeStream(CompleteStream);