I'm having trouble sending attachments via stream. I use Indy 10.6.2 and Delphi Berlin. The mail consists of html with attached images, plus one or more PDF files inserted directly from the database. I don't get any errors in the process. Mail is sent seamlessly, but attached PDFs are not received.
I look forward to comments
Msg := TIdMessage.Create(self);
try
Msg.ContentType := 'multipart/mixed';
Msg.From.Name := FromName;
Msg.From.Address := FromAddress;
Msg.Priority := mpHigh;
Msg.Subject := Asunto;
with TIdMessageBuilderHtml.Create do
try
if FMsgTxtPlnPac <> '' then
PlainText.Text := FMsgTxtPlnPac;
if FMsgHtmlPac <> '' then
begin
Html.Text := FMsgHtmlPac;
n := 0;
for s in FMsgHTMLFiles.Split([',']) do
begin
n := Succ(n);
c := 'img' + inttostr(n);
HTMLFiles.Add(s, c); // cid de imagen en HTML(cid:img1, cid:img2...)
end;
end;
if AttachFiles <> '' then
for s in AttachFiles.Split([',']) do
Attachments.Add(s);
// Attach from DB
while not dm.qryBlb.Eof do
begin
Attach := TIdAttachmentMemory.Create(Msg.MessageParts);
Attach.ContentType := 'application/pdf';
Attach.FileName := dm.qryBlb.FieldByName('nombre_archivo').AsString;
Attach.LoadFromStream(dm.GetDataBlbStrm('DATA_TXT')); // this ok in attach.datastream.size
Attach.CloseLoadStream;
dm.qryBlb.Next;
end;
FillMessage(Msg);
finally
Free;
end;
for s in FMailPac.Split([',']) do
begin
EmailAddress := Trim(s);
if EmailAddress <> '' then
begin
with Msg.recipients.Add do
begin
Address := EmailAddress;
end;
end;
end;
for s in MailCC.Split([',']) do
begin
EmailAddress := Trim(s);
if EmailAddress <> '' then
Msg.CCList.Add.Address := EmailAddress;
end;
for s in MailCCO.Split([',']) do
begin
EmailAddress := Trim(s);
if EmailAddress <> '' then
Msg.BccList.Add.Address := EmailAddress;
end;
finally
SMTP1.Send(Msg);
end;
TIdMessageBuilderHtml
supports adding attachments via streams, as well as via files. However, those streams have to remain alive for the duration that the TIdCustomMessageBuilder.Attachments
collection is populated, which is not an option in your case since you are looping through DB records one at a time, thus you would only be able to access 1 DB stream at a time.
You could create a local array/list of TMemoryStream
objects, and then populate the TMessageBuilderHtml
with those streams, but you will end up wasting a lot of memory that way since TIdMessageBuilderHtml
would make its own copy of the TMemoryStream
data. And there is no way to have TIdMessageBuilderHtml
just use your TMemoryStream
data as-is in a read-only mode (hmm, I wonder if I should add that feature!).
The reason why your manual TIdAttachmentMemory
objects don't work is simply because TIdCustomMessageBuilder.FillMessage()
clears the TIdMessage
's body before then re-populating it, thus losing your attachments (and various other properties that you are setting manually beforehand).
You would have to add your DB attachments to the TIdMessage
after FillMessage()
has done its work first. But, then you risk TIdMessageBuilderHtml
not setting up the TIdMessage
structure properly since it wouldn't know your DB attachments exist.
On a side note, you are not using TIdAttachmentMemory
correctly anyway. Do not call its CloseLoadStream()
method if you have not called its OpenLoadStream()
method first. Calling its LoadFromStream()
method is enough in this case (or, you can even pass the TStream
to TIdAttachmentMemory
's constructor). Do note, however, that you are leaking the TStream
returned by dm.GetDataBlbStrm()
.
So, in this case, you are probably better off simply populating the TIdMessage
manually and not use TIdMessageBuilderHtml
at all. Or, you could derive a new class from TIdMessageBuilderHtml
(or TIdCustomMessageBuilder
directly) and override its virtual FillBody()
and FillHeaders()
methods to take your DB streams into account.