【问题标题】:image in generated pdf corrupts pdf sent to server生成的 pdf 中的图像损坏了发送到服务器的 pdf
【发布时间】:2014-04-24 12:30:56
【问题描述】:

我正在使用 jsPDF 在 Web 应用程序中创建 PDF 文档,将该文档发送到 Perl,然后让 Perl 通过电子邮件发送它,它工作正常。但是,当我将图像添加到 PDF 文档时,它不再起作用,因为 Adob​​e Reader 说文件已损坏。该应用程序很大,因此这里有一个具有类似相关代码的存根,其行为方式相同:

html:

<!DOCTYPE html>
<html>
    <head>
        <script src="https://<myserver>/js/jquery.js"></script>
        <script src="https://<myserver>/js/jspdf.js"></script>
        <script src="https://<myserver>/js/jspdf.plugin.addimage.js"></script>      
        <script src="https://<myserver>/test/pdf.js"></script>
    </head>
    <body>
        <input type="submit" id="go">
    </body>
</html>

js:

$(document).ready(function() {
    $('#go').on('click',function() {
        //create PDF
        var imgData = 'data:image/jpeg;base64,<dataurlencoded image string>';
        var doc = new jsPDF('p','pt','a4');
        doc.addImage(imgData, 'JPEG', 22, 22, 138, 28);     
        doc.text(30, 120, 'Lorem Ipsum!');
        var perl_pdf = doc.output();

        //send PDF to perl and have perl email it
        $.ajax({
            type: "POST",
            url: "https://<myserver>/cgi-bin/pdf.pl", 
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
            dataType: "json",
            data: "perl_pdf="+encodeURIComponent(perl_pdf),
            error: function(XMLHttpRequest, textStatus, errorThrown) { 
                alert("error:  "+ XMLHttpRequest.responseText + ", textStatus: " + textStatus + ", errorThrown: " + errorThrown);
            }, 
            success: function(data){
                alert("Success: "+data.success);
            } 
       });
    });     
});

perl:

#!d:/perl/bin/perl.exe -w
use strict;
use warnings;
use CGI qw(:all);
use MIME::Lite;
use MIME::Base64;

my $q = CGI->new();
my $pdf_doc = $q->param('perl_pdf');

open (OUTFILE, '>pdf.pdf') or die "Could not open file";
binmode(OUTFILE);
print OUTFILE decode_base64($pdf_doc);
close OUTFILE;

my $from_address = '<from_address>';
my $to_address = '<to_address>';
my $mail_host = '<smtp_server>';

my $subject = 'PDF Test';
my $message_body = "The PDF is attached...\n\n";

my $my_file = 'pdf.pdf';
my $out_file = 'test.pdf';

my $msg = MIME::Lite->new (
    From => $from_address,
    To => $to_address,
    Subject => $subject,
    Type => 'multipart/mixed') or die "Cannot create multipart container:  $!\n";

$msg->attach (
    Type => 'TEXT',
    Data => $message_body) or die "Cannot attach text: $!\n";

$msg->attach (
    Type => 'application/pdf',
    Path => $my_file,
    Filename => $out_file,
    Disposition => 'attachment') or die "Cannot attach file: $!\n";

MIME::Lite->send('smtp', $mail_host, Timeout=>60);
$msg->send;

my $json = qq{{"success" : "This worked"}};
print $q->header(-type => "application/json", -charset => "utf-8");
print $json;

如果我将 Ajax 调用和输出创建替换为...

doc.output('dataurlnewwindow',{});    

...然后它会正确显示在新的浏览器选项卡中,因此我知道图像已正确插入。从我在搜索中发现的内容来看,这似乎是一些编码问题,但我还没有找到解决该问题的方法。如何将带有图像的 PDF 文档成功发送到服务器上的 Perl,以免损坏?

【问题讨论】:

  • 在不尝试分析脚本的情况下,您应该首先尝试使用 perl 脚本发送一个 100% ok pdf(例如已验证的内容)。如果它会发送 OK 问题出在 JS 中 - 如果不是 - 是时候检查 perl 脚本了;)
  • and.. 考虑:MIME::Lite 不被其当前的维护者推荐。 ...metacpan.org/pod/MIME::Lite
  • @jm666,请再次阅读我的原帖 - 如果 PDF 中没有图像,应用程序可以正常工作;文档被创建,发送到服务器,Perl 通过电子邮件发送它,收件人成功打开它。添加图像后,如果将 PDF 发送到浏览器,则会正确生成 PDF 并正确打开。但是,当有图像并将其发送到服务器时,它不起作用。同样,我发现的所有迹象都指向图像的编码问题 - base64 与 UTF-8,但我还没有找到解决此问题的解决方案。
  • 再次阅读我的评论。您确定保存的文件 pdf.pdf 确实包含正确的数据吗?我不是。您应该:1.)base64 在 JS 端对整个 pdf 进行编码,2.)将 base64 编码的字符串作为 POST 发送到服务器 3.)在 perl 脚本中解码 base64 以获取原始内容。因为 base64 只包含 ascii 你不应该关心编码。
  • 好的,我已经编辑了脚本 - 我在 Ajax 调用中的数据上使用 encodeURIComponent() 方法,然后在 Perl 接收到它后对其进行解码。生成的 PDF 文件仍然损坏。我无法判断编码是否破坏了它或解码。

标签: javascript ajax perl pdf jspdf


【解决方案1】:

此问题已在最新版本的 JSPDF 中得到解决。 如果您使用 xmlhttprequest.send 或作为 ajax 调用的一部分将带有图像的 PDF 作为表单数据发送到服务器。

请使用jsPDF.output('blob') 而不是jsPDF.output()

这不会在发送到服务器时损坏 pdf。

【讨论】:

    【解决方案2】:

    好的,这可能对你没有帮助,因为你使用的是 Windows,只是演示:

    我的测试 perl 脚本称为 app.pl:

    #!/usr/bin/env perl
    use strict;
    use warnings;
    use CGI qw(:all);
    
    my $q = CGI->new();
    my $pdf_doc = $q->param('perl_pdf');
    
    open (my $fp, '>', 'pdf.pdf') or die "Could not open file";
    print $fp $pdf_doc;
    close $fp;
    print "OK\n";
    

    有一个名为x.pdf的pdf文件。

    $ ls -l x.pdf
    -rw-r--r--@ 1 jm  staff  100838 18 mar 19:20 x.pdf
    

    在端口 3000 上运行一个简单的测试网络服务器

    plackup --port 3000 -MPlack::App::WrapCGI -e 'Plack::App::WrapCGI->new( script => "./app.pl", execute => 1)->to_app'
    

    说:

    HTTP::Server::PSGI: Accepting connections at http://0:3000/
    

    从另一个终端通过curl 命令发送base64 编码文件

    $ curl -i -F perl_pdf="$(base64 < x.pdf)" 0:3000/
    

    回复:

    HTTP/1.0 200 OK
    Date: Tue, 18 Mar 2014 20:15:40 GMT
    Server: HTTP::Server::PSGI
    Content-Length: 3
    
    OK
    

    服务器说:

    127.0.0.1 - - [18/Mar/2014:21:06:00 +0100] "POST / HTTP/1.1" 200 3 "-" "curl/7.35.0"
    

    并保存文件pdf.pdf该文件有base64编码的内容。

    $ ls -la pdf.pdf
    -rw-r--r--  1 jm  staff  134452 18 mar 21:06 pdf.pdf
    

    解码

    $ base64 -D < pdf.pdf >decoded.pdf
    

    并与原版比较

    $ cmp decoded.pdf x.pdf
    $ ls -la decoded.pdf 
    -rw-r--r--  1 jm  staff  100838 18 mar 21:18 decoded.pdf
    

    没有区别 - 发送 pdf - 成功。

    很遗憾,无法为您提供更多帮助,因为:

    • 您使用的是 Windows
    • 而我不懂 Javascript...

    考虑检查:

    【讨论】:

      【解决方案3】:

      我发现了问题。在将图像添加到 PDF 之前,将文件发送到 Perl 无需编码,显然是因为在发送字符串时没有(或没有相关的)二进制信息丢失。当然,添加图像向字符串中添加了非常相关的二进制信息,这些信息无法在 urlencoded 消息中发送。编码/解码应该已经解决了这个问题,但是......

      我尝试了许多不同的 Base64 编码/解码方法,但我的文件仍然损坏。我终于偶然发现了一个类似的问题,有人提到将字符串作为 URL 的一部分发送会将 + 符号转换为空格。我删除了 Perl 端的解码以查看编码字符串的样子,并且整个字符串中确实有几个空格。用

      将它们转换回来
      $pdf_doc =~ s/ /+/g;
      

      在让 Perl 写入文件之前修复了该问题。现在可以在服务器端的 Adob​​e 中提取该文件。

      【讨论】:

        最近更新 更多