Aharon Zilberman
Aharon Zilberman

Reputation: 27

Sending TMemoryStream as mail attachment

I am exporting a FastReport as PDFFile using TfrxPDFExport. I made a function that returns me a PDF as IStream:

Function TPDFReport.GetAsFile (): IStream;
var
  MStream: TMemoryStream;
Begin
  MStream := TMemoryStream.Create;
  try
    With formReport.Create (NIL) do begin
      frxPDFExport.Stream := MStream;
      frxReport.PrepareReport;
      frxReport.Export(frxPDFExport);
    end;
    Result := TStreamAdapter.Create(MStream) as IStream;
  finally

  end;
End;

Afterwards in a different unit I execute this function, get an IStream and load it into a TMemoryStream:

  PDFOleStream := TOleStream.Create(PDFReport.GetAsFile ());
  PDFMemoryStream.LoadFromStream(PDFOleStream);

Now I got the MemoryStream, but I don't really know the next step. I searched through web and found out that's not possible to send a Stream as a mail attachment with Indy. So I think, it's better to save a TMemoryStream as a temp file and then send it as an attachment. But as soon as I execute

PDFMemoryStream.SaveToFile('c:\\PDFExp.pdf');

I get an access violation. What have I done wrong? Thanks.

Upvotes: 0

Views: 704

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596352

You do not need to save the TMemoryStream to a file just to attach it to an email. Indy's TIdMessage component is flexible in the types of data you can attach to it.

For instance, Indy has a TIdAttachmentMemory class you can use, eg:

uses
 ..., IdAttachmentMemory;

var
  Attach: TIdAttachmentMemory;
begin
  ...

  Attach := TIdAttachmentMemory.Create(IdMessage1, PDFMemoryStream); // alternatively, use PDFOleStream instead...
  Attach.FileName := 'report.pdf';
  // set other properties as needed...

  // send the email...

  ...
end;

Note that TIdAttachmentMemory uses its own internal TMemoryStream and will copy the source TStream's data into that internal stream. If you want to avoid that copy and send your own TStream as-is, you can derive your own class from TIdAttachment for that, eg:

uses
  Classes, IdAttachment, IdMessageParts, IdGlobal;

type
  TIdAttachmentStream = class(TIdAttachment)
  protected
    FDataStream: TStream;
    FDataStreamBeforeLoadPosition: TIdStreamSize;
  public
    constructor Create(Collection: TIdMessageParts; Stream: TStream); reintroduce;
    property DataStream: TStream read FDataStream;
    function OpenLoadStream: TStream; override;
    procedure CloseLoadStream; override;
    procedure FinishTempStream; override;
    function PrepareTempStream: TStream; override;
  end;

constructor TIdAttachmentStream.Create(Collection: TIdMessageParts; Stream: TStream);
begin
  inherited Create(Collection);
  FDataStream := Stream;
end;

procedure TIdAttachmentStream.CloseLoadStream;
begin
  DataStream.Position := FDataStreamBeforeLoadPosition;
end;

function TIdAttachmentStream.OpenLoadStream: TStream;
begin
  FDataStreamBeforeLoadPosition := DataStream.Position;
  DataStream.Position := 0;
  Result := DataStream;
end;

procedure TIdAttachmentStream.FinishTempStream;
begin
  DataStream.Position := 0;
end;

function TIdAttachmentStream.PrepareTempStream: TStream;
begin
  DataStream.Size := 0;
  Result := DataStream;
end;
var
  Attach: TIdAttachmentStream;
begin
  ...

  Attach := TIdAttachmentStream.Create(IdMessage1, PDFMemoryStream); // alternatively, use PDFOleStream instead...
  Attach.FileName := 'report.pdf';
  // set other properties as needed...

  // send the email...

  ...
end;

Or, you can write a class to send the original IStream directly, eg:

uses
  Classes, IdAttachment, IdMessageParts, IdGlobal;

type
  TIdAttachmentIStream = class(TIdAttachment)
  protected
    FDataStream: TStream;
    FDataStreamBeforeLoadPosition: TIdStreamSize;
  public
    constructor Create(Collection: TIdMessageParts; Stream: IStream); reintroduce;
    destructor Destroy; override;
    property DataStream: TStream read FDataStream;
    function OpenLoadStream: TStream; override;
    procedure CloseLoadStream; override;
    procedure FinishTempStream; override;
    function PrepareTempStream: TStream; override;
  end;

constructor TIdAttachmentIStream.Create(Collection: TIdMessageParts; Stream: IStream);
begin
  inherited Create(Collection);
  FDataStream := TOleStream.Create(Stream);
end;

destructor TIdAttachmentIStream.Destroy;
begin
  FDataStream.Free;
  inherited;
end;

procedure TIdAttachmentIStream.CloseLoadStream;
begin
  DataStream.Position := FDataStreamBeforeLoadPosition;
end;

function TIdAttachmentIStream.OpenLoadStream: TStream;
begin
  FDataStreamBeforeLoadPosition := DataStream.Position;
  DataStream.Position := 0;
  Result := DataStream;
end;

procedure TIdAttachmentIStream.FinishTempStream;
begin
  DataStream.Position := 0;
end;

function TIdAttachmentIStream.PrepareTempStream: TStream;
begin
  DataStream.Size := 0;
  Result := DataStream;
end;
var
  Attach: TIdAttachmentIStream;
begin
  ...

  Attach := TIdAttachmentIStream.Create(IdMessage1, PDFReport.GetAsFile());
  Attach.FileName := 'report.pdf';
  // set other properties as needed...

  // send the email...

  ...
end;

Upvotes: 3

Related Questions