PDFTK与php://memory

4

问题:在execpassthru命令中是否可以使用php://memory

我可以在exec或passthru中使用php变量,但是我在php://memory方面遇到了麻烦。

背景:

我正在尝试通过PDFTK消除所有临时pdf文件的编写。
1)编写一个临时fdf文件
2)使用#1填充一个临时pdf文件
3)重复#1和#2以获得所有pdf文件
4)合并所有pdf文件。

这目前是有效的 - 但它会创建很多文件,并成为瓶颈。

我想通过利用虚拟文件php://memory来加快pdftk的速度。

首先,我正在尝试仅将#1中使用的fdf文件虚拟化。仅回答这个问题就足够了。

代码如下:

$fdf = 'fdf file contents here';
$tempFdfVirtual= fopen("php://memory", 'r+');
if(  $tempFdfVirtual ) {
  fwrite(  $tempFdfVirtual, $fdf);
} else {
    echo "Failure to open temporary fdf file";
    exit;
}
rewind( $tempFdfVirtual);
$url = "unfilled.pdf";
$temppdf_fn = "output.pdf"; 
$command = "pdftk $url fill_form  $tempFdfVirtual output $temppdf_fn flatten"; 
$error="";   
exec( $command, $error );
if ($error!="") {
    $_SESSION['err'] = $error;       
} else {
    $_SESSION['err'] = 0;
}

我遇到了错误代码#1。如果我使用stream_get_contents($tempFdfVirtual)函数,它会显示内容。

感谢查看!


只是提醒其他可能在寻找同样内容的人...我使用下面提供的答案完成了第一步。然后,我能够通过将pdftk输出到管道,并使用ZendPdf将所有输出流合并为一个pdf来完成步骤2-4的组合。PDFTK在合并虚拟pdf时只能接受一个虚拟流,但对于实际文件列表可以正常工作。ZendPdf的好处是您可以传递pdf数据而不是整个文件。 - Cymbals
2个回答

4

php://memoryphp://temp(以及实际上所有的文件描述符)仅适用于当前运行的PHP进程。此外,$tempFdfVirtual是一个资源句柄,因此在字符串中将其放置没有意义。

您应该通过标准输入将资源句柄中的数据传递给进程。您可以使用proc-open实现这一点,它比exec更能控制子进程的输入和输出。

请注意,由于某种原因,您无法将“php://memory”文件描述符传递给进程。PHP会抱怨:

Warning: proc_open(): cannot represent a stream of type MEMORY as a File Descriptor

改用php://temp,它应该完全相同,只是在流变得足够大时它会使用临时文件。

这是一个经过测试的示例,说明使用proc_open()的一般代码模式。这应该包装在函数或其他抽象中:

$testinput = "THIS IS A TEST STRING\n";

$fp = fopen('php://temp', 'r+');
fwrite($fp, $testinput);
rewind($fp);

$cmd = 'cat';
$dspec = array(
    0 => $fp,
    1 => array('pipe', 'w'),
);
$pp = proc_open($cmd, $dspec, $pipes);

// busywait until process is finished running.
do {
    usleep(10000);
    $stat = proc_get_status($pp);
} while($stat and $stat['running']);

if ($stat['exitcode']===0) {
    // index in $pipes will match index in $dspec
    // note only descriptors created by proc_open will be in $pipes
    // i.e. $dspec indexes with an array value.
    $output = stream_get_contents($pipes[1]);
    if ($output == $testinput) {
        echo "TEST PASSED!!";
    } else {
        echo "TEST FAILED!! Output does not match input.";
    }
} else {
    echo "TEST FAILED!! Process has non-zero exit status.";
}

// cleanup
// close pipes first, THEN close process handle.
foreach ($pipes as $pipe) {
    fclose($pipe);
}
// Only file descriptors created by proc_open() will be in $pipes.
// We still need to close file descriptors we created ourselves and
// passed to it.
// We can do this before or after proc_close().
fclose($fp);
proc_close($pp);

以下是针对您使用PDFTK的未经测试示例:

// Command takes input from STDIN
$command = "pdftk unfilled.pdf fill_form - output tempfile.pdf flatten"; 
$descriptorspec = array(
    0 => $tempFdfVirtual, // feed stdin of process from this file descriptor
//    1 => array('pipe', 'w'), // Note you can also grab stdout from a pipe, no need for temp file
);
$prochandle = proc_open($command, $descriptorspec, $pipes);
// busy-wait until it finishes running
do {
    usleep(10000);
    $stat = proc_get_status($prochandle);
} while ($stat and $stat['running']);

if ($stat['exitcode']===0) {
    // ran successfully
    // output is in that filename
    // or in the file handle in $pipes if you told the command to write to stdout.
}

// cleanup
foreach ($pipes as $pipe) {
   fclose($pipe);
}
proc_close($prochandle);

我对proc_open不太熟悉,请多包涵。当我通过php://memory创建的virtualfdf文件传递给proc_open时,我会得到proc_open() [<a href='function.proc-open'>function.proc-open</a>]:无法将MEMORY类型的流表示为文件描述符。如果我有一个包含字符串的变量$fdf - 我应该使用stdout创建一个虚拟文件,还是应该如何正确地将该“文件”传递到proc_open中? - Cymbals
啊,这真是个严重的问题。这是 PHP 的限制。使用 php://temp 代替 php://memory -- 在我的系统上似乎可以工作,尽管我不知道为什么会有差别。 - Francis Avila
它有效了!我尝试了建议的php://temp。哇...我还在https://dev59.com/s3E95IYBdhLWcg3wPrkI找到了类似的代码,但是注意到你不需要做类似于fwrite($pipes[0], stream_get_contents(STDIN));的事情。 - Cymbals
谢谢。展望未来:根据pdftk文档:使用“-”通过stdin将单个PDF传递到pdftk中。在cat命令上,我会有几个pdf文件。我是否只需使用多个破折号,并为每个设置一个流? - Cymbals
fillform 只会有一个单独的 PDF。我指的是上面列出的创建虚拟 PDF 的步骤 2-4。在最终的 cat 操作中,我将尝试使用管道而不是一系列文件名。 - Cymbals
显示剩余6条评论

1

问题不在于你使用了php://memory,而是任何文件句柄都是如此。文件句柄仅存在于当前进程中。就所有目的而言,从fopen返回的句柄不能传输到脚本之外的任何其他地方。

只要你正在使用外部应用程序,你基本上只能使用临时文件。你唯一的选择是尝试将数据通过stdin传递给pdftk,并在stdout上检索输出(如果它支持)。据我所知,调用具有对其描述符(stdin/stdout)这种访问权限的外部进程的唯一方法是使用proc_函数族,特别是proc_open


谢谢你,昨天你快速的回复给了我指引,让我开始阅读proc_open相关的资料。 - Cymbals

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接