I am attempting to export a report as a PDF then attach it to an Email and/or MMS. I base64 encode the TFileStream to TStringStream, then attach it to an email but cannot open it. Using the same TStringStream, I attach it to an MMS and it works as expected. The Indy method seems to work with a stream, what am I doing wrong?
Edit: The email attachment and the email message body appear to in the file that's attached which is why it isn't readable. If I base64 decode the contents of what should be the attachment, it works.
Any ideas why this is happening?
Email Attachment
MMS Attachment
ReportPdf := TStringStream.Create;
try
if (CreateReportPdf(QuickRep1, ReportPdf, QryInfoPrintNumber.AsString, 'REPORT', IsReprint)) then
begin
if (Assigned(ReportPdf)) then
begin
SendReceiptEmail(ReportPdf, QryInfoPrintNumber.AsString, 'REPORT', QryReportName.AsString, 'Test', QryInfoReplyToEmailAddress.AsString);
SendReceiptSMS(ReportPdf, QryInfoPrintNumber.AsString, 'REPORT', QryReportName.AsString, 'Test');
end;
end;
finally
ReportPdf.Free;
end;
Here's where the report is exported. This works as expected.
function CreateReportPdf(QuickRep: TQuickRep; var ReportPdf: TStringStream; const PrintNumber, ReportName: string; const IsReprint: Boolean): Boolean;
var
aPdf: TQRPDFDocumentFilter;
tmpPath, tmpFileName: string;
fs: TFileStream;
begin
Result := False;
if (not Assigned(QuickRep)) then
Exit;
tmpPath := GetSpecialFolderPath(CSIDL_LOCAL_APPDATA) + '\Temp\';
tmpFileName := CreateTmpFileName(tmpPath, Format('%s_%s', [ReportName, PrintNumber]), '.pdf');
aPdf := TQRPDFDocumentFilter.Create(tmpPath + tmpFileName);
fs := nil;
try
try
aPdf.CompressionOn := True;
aPdf.PageLength := 11;
QuickRep.ExportToFilter(aPdf);
fs := TFileStream.Create(tmpPath + tmpFileName, fmOpenRead);
fs.Position := 0;
TNetEncoding.Base64.Encode(fs, ReportPdf);
Result := True;
except
ShowMessage('Failed to create report PDF.');
Result := False;
end;
finally
aPdf.Free;
fs.Free;
end;
end;
I've tried decoding the base64 (see below) -- didn't help.
procedure SendReportEmail(ReportPdf: TStringStream; const PrintNumber, ReportName, Recipients, MessageText, ReplyTo: string);
var
Attachment: TStringList;
Host: string;
Port: Integer;
// ReportPdfDecoded: TStringStream;
I: Integer;
begin
if (ReportPdf.DataString.Length > 0) then
begin
ReportPdf.Position := 0;
// ReportPdfDecoded := TStringStream.Create;
Attachment := TStringList.Create();
try
// TNetEncoding.Base64.Decode(ReportPdf, ReportPdfDecoded);
// Attachment.AddObject('stream/pdf', ReportPdfDecoded);
Attachment.AddObject('stream/pdf', ReportPdf);
Host := GetConfigValue(cCFG_EMAIL_HOST).AsString;
Port := GetConfigValue(cCFG_EMAIL_PORT).AsInteger;
From := GetConfigValue(cCFG_EMAIL_FROM).AsString;
Lib.SendEmail(Host, From, Recipients, ReportName + '_' + PrintNumber,
MessageText, '', '', ReplyTo, Port, False, Attachment);
finally
for I := Attachment.Count -1 downto 0 do
Attachment.Objects[I].Free;
Attachment.Free;
// ReportPdfDecoded.Free;
end;
end;
end;
Am I missing something obvious here? Thanks for looking.
procedure SendEmail(Host, From, Recipients, Subject, Body, CC, BCC, ReplyTo: string; Port: Integer; IsBodyHtml: Boolean; Attachments: TStrings);
var
IdSMTP: TIdSMTP;
IdMessage: TIdMessage;
builder: TIdCustomMessageBuilder;
s: Integer;
begin
IdSMTP := TIdSMTP.Create(nil);
IdMessage := TIdMessage.Create(nil);
try
if IsBodyHtml then
begin
builder := TIdMessageBuilderHtml.Create;
TIdMessageBuilderHtml(builder).Html.Text := Body
end
else
begin
builder := TIdMessageBuilderPlain.Create;
end;
try
if (Assigned(Attachments)) then
begin
IdMessage.ContentType := 'multipart/mixed';
for s := 0 to Attachments.Count -1 do
begin
if (Attachments.Strings[s] = 'stream/pdf') then
begin
builder.PlainTextCharSet := 'utf-8';
builder.Attachments.Add(TStringStream(attachments.Objects[s]), 'application/pdf');
end
else
builder.Attachments.Add(attachments.ValueFromIndex[s]);
end;
end;
builder.FillMessage(IdMessage);
finally
builder.Free;
end;
IdMessage.From.Address := From;
IdMessage.Recipients.EMailAddresses := Recipients;
IdMessage.Subject := Subject;
IdMessage.Body.Text := Body;
IdMessage.CCList.EMailAddresses := CC;
IdMessage.BccList.EMailAddresses := BCC;
IdMessage.ReplyTo.EMailAddresses := ReplyTo;
if not IsBodyHtml then
IdMessage.Body.Text := Body;
try
IdSMTP.Host := Host;
IdSMTP.Port := Port;
IdSMTP.Connect;
IdSMTP.Send(IdMessage);
IdSMTP.Disconnect;
finally
IdSMTP.Free;
end;
finally
IdMessage.Free;
end;
end;
There is no need to manually base64-encode the PDF before emailing it with Indy (if you need base64 for SMS, you should handle that separately at the point where you are sending the SMS). Indy's TIdMessage
component can handle the base64 for you, so simply attach the original PDF as-is and set its ContentTransfer
property to 'base64'
. Since you are storing the PDF into a TStream
and then attaching that to an email, you should store just the raw bytes of the PDF (ie, using TMemoryStream
or TFileStream
), do not store a pre-encoded version of the PDF.
Try something more like this:
ReportPdf := TMemoryStream.Create;
try
if CreateReportPdf(QuickRep1, ReportPdf, QryInfoPrintNumber.AsString, 'REPORT', IsReprint) then
begin
SendReceiptEmail(ReportPdf, QryInfoPrintNumber.AsString, 'REPORT', QryReportName.AsString, 'Test', QryInfoReplyToEmailAddress.AsString);
...
end;
finally
ReportPdf.Free;
end;
function CreateReportPdf(QuickRep: TQuickRep; ReportPdf: TMemoryStream; const PrintNumber, ReportName: string; const IsReprint: Boolean): Boolean;
var
aPdf: TQRPDFDocumentFilter;
tmpPath, tmpFileName: string;
begin
Result := False;
if not Assigned(QuickRep) then
Exit;
tmpPath := GetSpecialFolderPath(CSIDL_LOCAL_APPDATA) + '\Temp\';
tmpFileName := CreateTmpFileName(tmpPath, Format('%s_%s', [ReportName, PrintNumber]), '.pdf');
try
aPdf := TQRPDFDocumentFilter.Create(tmpPath + tmpFileName);
try
aPdf.CompressionOn := True;
aPdf.PageLength := 11;
QuickRep.ExportToFilter(aPdf);
ReportPdf.LoadFromFile(tmpPath + tmpFileName);
Result := True;
finally
aPdf.Free;
end;
except
ShowMessage('Failed to create report PDF.');
end;
end;
procedure SendReportEmail(ReportPdf: TMemoryStream; const PrintNumber, ReportName, Recipients, MessageText, ReplyTo: string);
var
Attachment: TStringList;
Host, From: string;
Port: Integer;
begin
if ReportPdf.Size > 0 then
begin
ReportPdf.Position := 0;
Attachment := TStringList.Create;
try
Attachment.AddObject('stream/pdf', ReportPdf);
Host := GetConfigValue(cCFG_EMAIL_HOST).AsString;
Port := GetConfigValue(cCFG_EMAIL_PORT).AsInteger;
From := GetConfigValue(cCFG_EMAIL_FROM).AsString;
Lib.SendEmail(Host, From, Recipients, ReportName + '_' + PrintNumber,
MessageText, '', '', ReplyTo, Port, False, Attachment);
finally
Attachment.Free;
end;
end;
end;
procedure SendEmail(Host, From, Recipients, Subject, Body, CC, BCC, ReplyTo: string; Port: Integer; IsBodyHtml: Boolean; Attachments: TStrings);
var
IdSMTP: TIdSMTP;
IdMessage: TIdMessage;
builder: TIdMessageBuilderHtml;
attach: TIdMessageBuilderAttachment;
I: Integer;
begin
IdMessage := TIdMessage.Create(nil);
try
builder := TIdMessageBuilderHtml.Create;
try
builder.PlainTextCharSet := 'utf-8';
builder.HtmlCharSet := 'utf-8';
if IsBodyHtml then
builder.Html.Text := Body
else
builder.PlainText.Text := Body;
if Assigned(Attachments) then
begin
for I := 0 to Attachments.Count - 1 do
begin
if Attachments.Strings[I] = 'stream/pdf' then
begin
attach := builder.Attachments.Add(TMemoryStream(attachments.Objects[I]), 'application/pdf');
attach.ContentTransfer := 'base64';
else
attach := builder.Attachments.Add(attachments.ValueFromIndex[I]);
// optional: set attach.WantedFileName or attach.FileName if desired...
end;
end;
builder.FillMessage(IdMessage);
finally
builder.Free;
end;
IdMessage.From.Address := From;
IdMessage.Recipients.EMailAddresses := Recipients;
IdMessage.Subject := Subject;
IdMessage.CCList.EMailAddresses := CC;
IdMessage.BccList.EMailAddresses := BCC;
IdMessage.ReplyTo.EMailAddresses := ReplyTo;
IdSMTP := TIdSMTP.Create(nil);
try
IdSMTP.Host := Host;
IdSMTP.Port := Port;
IdSMTP.Connect;
try
IdSMTP.Send(IdMessage);
finally
IdSMTP.Disconnect;
end;
finally
IdSMTP.Free;
end;
finally
IdMessage.Free;
end;
end;