Search code examples
delphiencodingmimeindydelphi-xe7

How to encode binary data with Indy Mime?


I have an Ansi string that I use to store binary data - bytes in 0-255 range (I know it should be a Byte array or so, but it is not much difference between them).
I want to pass this "binary string" through Indy MIME (TIdEncoderMIME.EncodeString / TIdDecoderMIME.DecodeString) and obtain a human-readable ANSI string.

I thought that the output of Encode/DecodeString will be a string that has only ANSI characters in it if I use IndyTextEncoding_8Bit encoding. But I was wrong!

so, how to encode binary data with Indy Mime (something similar to application/octet-stream)?


Solution

  • DONT use AnsiString for binary data!

    AnsiString is not an appropriate container for binary data, especially in a Unicode environment like XE7. Use a proper byte container, like T(Id)Bytes or TMemoryStream instead.

    You can't pass AnsiString as-is through the TId(Encoder|Decoder)MIME string methods, only UnicodeString, so implicit RTL Ansi<->Unicode conversions are likely to corrupt your binary data. Use the binary-oriented methods instead ((Encode|Decode)Bytes(), (Encode|Decode)Stream()). They exist for a reason.

    That being said, Indy 10 does have a TIdMemoryBufferStream class (desktop platforms only), so if you MUST use AnsiString (and you really shouldn't), you can wrap it in a TStream interface without having to make additional copies of data in memory. For example:

    var
      Binary: AnsiString;
      Strm: TIdMemoryBufferStream;
      Base64: String;
    begin
      Binary := ...; // binary data
      Strm := TIdMemoryBufferStream.Create(PAnsiChar(Binary), Length(Binary));
      try
        Base64 := TIdEncoderMIME.EncodeStream(Strm);
      finally
        Strm.Free;
      end;
      // use Base64 as needed...
    end;
    

    var
      Base64: String;
      Strm: TIdMemoryBufferStream;
      Binary: AnsiString;
    begin
      Base64 := ...; // encoded data
      SetLength(Binary, (Length(Base64) div 4) * 3);
      Strm := TIdMemoryBufferStream.Create(PAnsiChar(Binary), Length(Binary));
      try
        TIdDecoderMIME.DecodeStream(Base64, Strm);
        SetLength(Binary, Strm.Size);
        SetCodePage(PRawByteString(@Binary)^, 28591, False);
      finally
        Strm.Free;
      end;
      // use Binary as needed...
    end;