1

オンザフライでも生成される HTML からオンザフライで注文領収書の PDF を生成し、それを誰かに電子メールで送信しようとしています。

ファイルを作成して電子メールに添付してからファイルを削除したくないので、HTMLをSTDIN経由で(Perlから)wkhtmltopdfに送信し、wkhtmltopdfからのPDF出力を電子メールでキャプチャしようとしていますMIME::Lite Email::Mimeを使用して添付します。

これは、Perl を使用して人々が動的に生成された PDF ファイルを私の Web サイトからダウンロードできるようにするために絶対に機能しますが、 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 出力をバッファリングせず、1 行ずつ送信しないことにあるようです。STDIN から HTML 入力を取得するとすぐに、すべての PDF 出力を STDOUT にストリーミングします。

これが、open2またはopen3を機能させることができなかった理由だと思います。

も試しopen (my $pdfOutput, "echo \"$htmlToPrint\"| wkhtmltopdf - -|")ましたが、これはシェルで実行されるため$htmlToPrint、引用符で囲まれていても、コマンドは HTML で使用されている記号をチョークします。

誰かがこれが役立つことを願っています...

4

1 に答える 1

1

入力をcmdに送信し、バックティックを使用せずにその出力を収集するには、 open2またはopen3を使用する必要があります。

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 に答える