1

我正在尝试从也即时生成的 HTML 即时生成订单收据的 PDF,然后通过电子邮件将其发送给某人。

我真的不想创建文件,将其附加到电子邮件,然后删除文件,所以我试图通过 STDIN(来自 Perl)将 html 发送到 wkhtmltopdf,然后在电子邮件中捕获来自 wkhtmltopdf 的 PDF 输出附件使用MIME::Lite Email::Mime。

绝对适用于使用 Perl 允许人们从我的网站下载动态生成的 PDF 文件,但尝试将其与MIME::Lite Email::Mime 一起使用是行不通的。(它可能会奏效,但由于它已经过时,我们使用 Email::Mime 代替)

我绝对确定这是由于我对使用文件句柄、管道、反引号和其他不常用的东西缺乏基本的理解,我很想更好地掌握这些东西。

这是有效的:

#!/usr/bin/perl
#### takes string containing HTML and outputs PDF to browser to download
#### (otherwise would output to STDOUT)

print "Content-Disposition: attachment; filename='testPDF.pdf'\n";
print "Content-type: application/octet-stream\n\n";

my $htmlToPrint = "<html>a bunch of html</html>";

### open a filehandle and pipe it to wkhtmltopdf
### *the arguments "- -" tell wkhtmltopdf to get 
###  input from STDIN and send output to STDOUT*
open(my $makePDF, "|-", "wkhtmltopdf", "-", "-") || die("$!");
print $makePDF $htmlToPrint;  ## sends my HTML to wkhtmltopdf which streams immediately to STDOUT

exit 1;

您可以从 Apache 运行它,它会向用户显示一个下载对话框,并下载一个名为“testPDF.pdf”的可读、正确的 pdf。

编辑:解决方案是Capture::Tiny模块(和Email::Mime):

#!/usr/bin/perl
use Capture::Tiny qw( capture );
use Email::Sender::Simple;
use Email::MIME::Creator;

my $htmlToPrint = "<html>a bunch of html</html>";

### it's important to capture STDERR as well, since wkhtmltopdf outputs
### its console messages on STDERR instead of STDOUT, so it can output
### the PDF to STDOUT; otherwise it will spam your error log    
(my $pdfstream, my $consoleOutput, my @retvals) = capture {
    open(my $makePDF, "|-", "wkhtmltopdf", "-", "-") || die("$!");
    print $makePDF $htmlToPrint;
};

my @parts = (
Email::MIME->create(
    attributes => {
        content_type => "text/plain",
        disposition  => "inline",
        charset      => "US-ASCII",
        encoding     => "quoted-printable",
    },
    body_str => "Your order receipt is attached as a PDF.",
),
Email::MIME->create(
    attributes => {
        filename     => "YourOrderReceipt.pdf",
        content_type => "application/pdf",
        disposition  => "attachment",
        encoding     => "base64",  ## base64 is ESSENTIAL, binary and quoted-printable do not work!
        name         => "YourOrderReceipt.pdf",
    },
    body => $pdfstream,
),
);

my $email = Email::MIME->create(
  header_str => [
      From => 'Some Person <me@mydomain.com>',
      To   => 'customer@theirdomain.com',
      Subject => "Your order receipt is attached...",
  ],
  parts => [ @parts ],
);

Email::Sender::Simple->send($email);
exit 1;

这一切现在都很完美。

大多数问题似乎是 wkhtmltopdf 没有缓冲 PDF 输出并逐行发送;一旦从 STDIN 获得 HTML 输入,它就会立即将所有 PDF 输出流式传输到 STDOUT。

我认为这就是我无法让 open2 或 open3 工作的原因。

我也试过open (my $pdfOutput, "echo \"$htmlToPrint\"| wkhtmltopdf - -|"),但这在 shell 中运行,所以即使用$htmlToPrint引号括起来,命令也会阻塞 HTML 中使用的符号。

希望有人觉得这有帮助...

4

1 回答 1

1

您需要使用 open2 或open3将输入发送到 cmd,然后在不使用反引号的情况下收集其输出。

local(*HIS_IN, *HIS_OUT, *HIS_ERR);
my $pid = open3(*HIS_IN, *HIS_OUT, *HIS_ERR,'wkhtmltopdf', '-', '-');
waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;

您可以使用更多新鲜的替代方案来发送电子邮件:

  use Email::MIME::Creator;
  use IO::All;

  # multipart message
  my @parts = (
      Email::MIME->create(
          attributes => {
              filename     => "report.pdf",
              content_type => "application/pdf",
              encoding     => "quoted-printable",
              name         => "2004-financials.pdf",
          },
          #body => io( *HIS_OUT )->all, it may work
          body => *HIS_OUT,

      ),
      Email::MIME->create(
          attributes => {
              content_type => "text/plain",
              disposition  => "attachment",
              charset      => "US-ASCII",
          },
          body_str => "Hello there!",
      ),
  );

  my $email = Email::MIME->create(
      header_str => [ From => 'casey@geeknest.com' ],
      parts      => [ @parts ],
  );
  # standard modifications
  $email->header_str_set( To            => rcpts()        );

  use Email::Sender::Simple;
  Email::Sender::Simple->send($email);
于 2013-01-30T09:22:04.720 回答