wkhtmltopdf โดยใช้ STDIN และ STDOUT ในภาษา Perl พร้อม Email::Mime

ฉันกำลังพยายามสร้าง PDF สำหรับใบเสร็จรับเงินการสั่งซื้อทันทีจาก HTML ที่สร้างขึ้นทันที จากนั้นจึงส่งอีเมลถึงบุคคลอื่น

ฉันไม่ต้องการสร้างไฟล์จริงๆ แนบไปกับอีเมลแล้วลบไฟล์ ดังนั้นฉันจึงพยายามส่ง html ไปยัง wkhtmltopdf ผ่าน STDIN (จาก Perl) จากนั้นจับเอาต์พุต PDF จาก wkhtmltopdf ในอีเมล ไฟล์แนบโดยใช้ MIME::Lite Email::Mime

ใช้งานได้อย่างสมบูรณ์ โดยใช้ Perl เพื่อให้ผู้คนดาวน์โหลดไฟล์ PDF ที่สร้างขึ้นแบบไดนามิกจากเว็บไซต์ของฉัน แต่พยายามใช้กับ MIME::Lite Email::Mime ไม่ได้' ไม่ทำงาน (มันอาจจะได้ผล แต่เนื่องจากมันล้าสมัยแล้ว เราเลยใช้ Email::Mime แทน)

ฉันแน่ใจอย่างยิ่งว่านี่เป็นเพราะฉันขาดความเข้าใจพื้นฐานในการทำงานกับตัวจัดการไฟล์ ไปป์ backticks และสิ่งอื่นๆ ที่ไม่ค่อยได้ใช้ และฉันก็อยากจะเข้าใจสิ่งเหล่านี้ให้มากขึ้น

นี่คือสิ่งที่ได้ผล:

#!/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 และจะแสดงกล่องโต้ตอบการดาวน์โหลดแก่ผู้ใช้ และดาวน์โหลดไฟล์ PDF ที่ถูกต้องที่อ่านได้ซึ่งมีชื่อว่า 'testPDF.pdf'

แก้ไข: วิธีแก้ไขคือโมดูล Capture::Tiny (และ อีเมล::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 <[email protected]>',
      To   => '[email protected]',
      Subject => "Your order receipt is attached...",
  ],
  parts => [ @parts ],
);

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

ทั้งหมดนี้ทำงานได้อย่างสมบูรณ์แบบในขณะนี้

ปัญหาส่วนใหญ่ดูเหมือนว่า wkhtmltopdf ไม่ได้บัฟเฟอร์เอาต์พุต PDF และส่งทีละบรรทัด มันจะสตรีมเอาต์พุต PDF ทั้งหมดไปยัง STDOUT ทันทีที่ได้รับอินพุต HTML จาก STDIN

ฉันคิดว่านี่คือสาเหตุที่ฉันไม่สามารถเปิด open2 หรือ open3 ได้

ฉันลอง open (my $pdfOutput, "echo \"$htmlToPrint\"| wkhtmltopdf - -|") ด้วย แต่สิ่งนี้ทำงานในเชลล์ ดังนั้นถึงแม้จะมี $htmlToPrint อยู่ในเครื่องหมายคำพูด คำสั่งก็ยังควบคุมสัญลักษณ์ที่ใช้ใน HTML

หวังว่าบางคนพบว่าสิ่งนี้มีประโยชน์ ...


person waldo22    schedule 30.01.2013    source แหล่งที่มา
comment
ประโยคแรกของเอกสาร MIME::Lite ไม่แนะนำให้ใช้ MIME::Lite   -  person jordanm    schedule 30.01.2013
comment
ขอบคุณ @jordanm ฉันใช้เวลานานในการอ่านเอกสาร แต่ก็พลาดไป   -  person waldo22    schedule 30.01.2013
comment
ฉันอัปเดตโพสต์นี้ด้วยรหัสการทำงาน ฉันเชื่อว่า MIME::Lite น่าจะได้ผลเช่นกัน แต่ฉันคิดว่าการใช้ Email::MIME ที่ใหม่กว่าที่แนะนำนั้นเป็นสิ่งที่ดี ปัญหาคือคุณไม่สามารถสื่อสารแบบสองทิศทางกับไปป์ได้ คุณต้องมีโมดูลเช่น Capture::Tiny ไฟล์แนบอีเมลต้องมีการเข้ารหัส base64 จึงจะทำงานได้ พยายาม binary และ quoted-printable แล้วไม่มีผล   -  person waldo22    schedule 02.02.2013


คำตอบ (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 => '[email protected]' ],
      parts      => [ @parts ],
  );
  # standard modifications
  $email->header_str_set( To            => rcpts()        );

  use Email::Sender::Simple;
  Email::Sender::Simple->send($email);
person user1126070    schedule 30.01.2013
comment
สามารถทำได้โดยใช้ backticks หรือไม่? ถ้าเป็นเช่นนั้นทำไมคุณถึงเลือก open3 มากกว่า backticks หรือในทางกลับกัน? - person waldo22; 31.01.2013
comment
เป็นไปไม่ได้ ว. แบ็คติค คุณต้องการสตรีมเนื้อหาไปยัง STDIN และคุณต้องการบันทึกเอาต์พุตโดยไม่ต้องใช้ไฟล์ชั่วคราว บางทีคุณอาจทำอะไรแบบนี้: open(OUT,echo $html| wkhtmltopdf - -|) or die; แต่จะใช้กับไฟล์ขนาดใหญ่ไม่ได้ - person user1126070; 31.01.2013
comment
ฉันจะลองดู ดูเหมือนว่าทั้ง open2 และ open3 จะไม่ทำงานในขณะนี้ ฉันคิดว่าเป็นเพราะ wkhtmltopdf สตรีมเอาต์พุตแทนที่จะเขียนทีละบรรทัด ... - person waldo22; 01.02.2013
comment
ตกลง ฉันแก้ปัญหานี้แล้ว แต่ฉันคิดว่ามันต้องมีคำตอบใหม่ คำแนะนำ Email::MIME ของคุณดีมาก open(OUT,"echo $html| wkhtmltopdf - -|") ใช้งานได้กับ IO แต่เนื่องจากใช้เชลล์ จึงทำให้ต้องใช้อักขระพิเศษใน HTML ดังนั้นจึงใช้ไม่ได้ คำแนะนำ open2/open3 ของคุณทำให้ฉันมีคำถามนี้: ลิงก์ ซึ่งนำฉันไปสู่ ​​Capture ::โปรแกรมจิ๋ว ซึ่งช่วยแก้ปัญหาของฉันได้ ฉันได้อัปเดตโค้ดของฉันด้านบนแล้ว - person waldo22; 02.02.2013
comment
ฉันโพสต์รหัสการทำงานในคำถามเดิมของฉัน จะมีประโยชน์มากกว่าถ้าแก้ไขโพสต์ของคุณและยอมรับว่าเป็นคำตอบ หรือสำหรับฉันที่จะโพสต์คำตอบแยกต่างหากและยอมรับสิ่งนั้น - person waldo22; 02.02.2013