英文:
Delphi Indy 10 pdf attachment is unreadable
问题
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展开收缩 = 'stream/pdf') then
begin
builder.PlainTextCharSet := 'utf-8';
builder.Attachments.Add(TStringStream(attachments.Objects展开收缩), 'application/pdf');
end
else
builder.Attachments.Add(attachments.ValueFromIndex展开收缩);
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;
英文:
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展开收缩 = 'stream/pdf') then
begin
builder.PlainTextCharSet := 'utf-8';
builder.Attachments.Add(TStringStream(attachments.Objects展开收缩), 'application/pdf');
end
else
builder.Attachments.Add(attachments.ValueFromIndex展开收缩);
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;
答案1
得分: 4
无需在使用Indy发送电子邮件之前手动对PDF进行base64编码(如果需要在短信中使用base64,您应该在发送短信的时候单独处理)。 Indy的TIdMessage
组件可以为您处理base64编码,所以只需附加原始PDF并将其ContentTransfer
属性设置为'base64'
。由于您将PDF存储到TStream
中,然后将其附加到电子邮件中,所以应该只存储PDF的原始字节(即使用TMemoryStream
或TFileStream
),不要存储PDF的预编码版本。
尝试像这样更改代码:
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]);
// 可选:如果需要的话设置 attach.WantedFileName 或 attach.FileName...
end;
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;
这些是代码段的中文翻译,希望对您有所帮助。
英文:
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;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论