Delphi Indy 10的PDF附件无法阅读。

huangapple go评论94阅读模式
英文:

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的原始字节(即使用TMemoryStreamTFileStream),不要存储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;

huangapple
  • 本文由 发表于 2023年7月31日 23:53:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76805243.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定