PhantomJS:将PDF导出到stdout

Dan*_*rzo 19 pdf pipe file-descriptor io-redirection phantomjs

有没有办法在PhantomJS中触发PDF导出功能而不指定扩展名为.pdf的输出文件?我们想stdout用来输出PDF.

phi*_*reo 20

您可以直接输出到stdout而无需临时文件.

page.render('/dev/stdout', { format: 'pdf' });

请参阅此处了解添加此内容的历史记录.

如果要从stdin获取HTML并将PDF输出到stdout,请参阅此处

  • 出于某种原因,它适用于Mac OS X,但它不适用于Linux(PhantomJS版本1.9.8). (2认同)

Poo*_*imi 19

对不起,回答非常长; 我有一种感觉,我生命中需要多次参考这种方法,所以我会写"一个答案来统治它们".我将首先对文件,文件描述符,(命名)管道和输出重定向进行一些讨论,然后回答您的问题.


考虑一下这个简单的C99程序:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{

  if (argc < 2) {
    printf("Usage: %s file_name\n", argv[0]);
    return 1;
  }

  FILE* file = fopen(argv[1], "w");
  if (!file) {
    printf("No such file: %s\n", argv[1]);
    return 2;
  }

  fprintf(file, "some text...");

  fclose(file); 

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

非常直截了当.它接受一个参数(文件名)并在其中输入一些文本.不能再简单了.


clang write_to_file.c -o write_to_file.o或编译它gcc write_to_file.c -o write_to_file.o.

现在,运行./write_to_file.o some_file(打印到some_file).然后跑cat some_file.结果如预期的那样some text...

现在让我们变得更加花哨.键入(./write_to_file.o /dev/stdout) > some_file在终端中.我们要求程序写入其标准输出(而不是常规文件),然后我们将其重定向stdoutsome_file(使用> some_file).我们可以使用以下任何一个来实现这一目标:

  • (./write_to_file.o /dev/stdout) > some_file,意思是"使用stdout"

  • (./write_to_file.o /dev/stderr) 2> some_file,表示"使用stderr,并使用重定向2>"

  • (./write_to_file.o /dev/fd/2) 2> some_file,与上述相同; stderr是默认情况下分配给Unix进程的第三个文件描述符(在stdin和之后stdout)

  • (./write_to_file.o /dev/fd/5) 5> some_file,表示"使用您的第六个文件描述符,并将其重定向到some_file"

如果不清楚,我们使用的是Unix管道而不是实际文件(毕竟,一切都是Unix中的文件).我们可以使用此管道做各种奇特的事情:将其写入文件,或将其写入命名管道并在不同进程之间共享.


现在,让我们创建一个命名管道:

mkfifo my_pipe
Run Code Online (Sandbox Code Playgroud)

如果你ls -l现在输入,你会看到:

total 32
prw-r--r--  1 pooriaazimi  staff     0 Jul 15 09:12 my_pipe
-rw-r--r--  1 pooriaazimi  staff   336 Jul 15 08:29 write_to_file.c
-rwxr-xr-x  1 pooriaazimi  staff  8832 Jul 15 08:34 write_to_file.o
Run Code Online (Sandbox Code Playgroud)

注意第二行开头的p.这意味着它my_pipe是一个(命名)管道.

现在,让我们指定我们想要对管道做什么:

gzip -c < my_pipe > out.gz &
Run Code Online (Sandbox Code Playgroud)

它意味着:gzip我放在里面my_pipe并写下结果out.gz.在&末来要求在后台运行此命令的外壳.你会得到类似的东西[1] 10449,控制权会回到终端.

然后,只需将我们的C程序的输出重定向到此管道:

(./write_to_file.o /dev/fd/5) 5> my_pipe
Run Code Online (Sandbox Code Playgroud)

要么

./write_to_file.o my_pipe
Run Code Online (Sandbox Code Playgroud)

你会得到

[1]+  Done                    gzip -c < my_pipe > out.gz
Run Code Online (Sandbox Code Playgroud)

这意味着gzip命令已经完成.

现在,做另一个ls -l:

total 40
prw-r--r--  1 pooriaazimi  staff     0 Jul 15 09:14 my_pipe
-rw-r--r--  1 pooriaazimi  staff    32 Jul 15 09:14 out.gz
-rw-r--r--  1 pooriaazimi  staff   336 Jul 15 08:29 write_to_file.c
-rwxr-xr-x  1 pooriaazimi  staff  8832 Jul 15 08:34 write_to_file.o
Run Code Online (Sandbox Code Playgroud)

我们已成功gzip编辑了我们的文字!

执行gzip -d out.gz解压缩此gziped文件.它将被删除,out并将创建一个新文件().cat out得到我们:

some text...
Run Code Online (Sandbox Code Playgroud)

这是我们的预期.

不要忘记拆下管道rm my_pipe!


现在回到PhantomJS.

这是一个简单的PhantomJS脚本(render.coffee用CoffeeScript编写),它带有两个参数:URL和文件名.它加载URL,呈现它并将其写入给定的文件名:

system = require 'system'

renderUrlToFile = (url, file, callback) ->
  page = require('webpage').create()
  page.viewportSize = { width: 1024, height : 800 }
  page.settings.userAgent = 'Phantom.js bot'

  page.open url, (status) ->
    if status isnt 'success'
      console.log "Unable to render '#{url}'"
    else
      page.render file

    delete page
    callback url, file


url         = system.args[1]
file_name   = system.args[2]

console.log "Will render to #{file_name}"
renderUrlToFile "http://#{url}", file_name, (url, file) ->
  console.log "Rendered '#{url}' to '#{file}'"
  phantom.exit()
Run Code Online (Sandbox Code Playgroud)

现在输入phantomjs render.coffee news.ycombinator.com hn.png终端将Hacker News首页渲染到文件中hn.png.它按预期工作.那样做phantomjs render.coffee news.ycombinator.com hn.pdf.

让我们重复我们之前在C程序中所做的事情:

(phantomjs render.coffee news.ycombinator.com /dev/fd/5) 5> hn.pdf
Run Code Online (Sandbox Code Playgroud)

它不起作用...... :(为什么?因为,正如PhantomJS的手册所述:

渲染(文件名)

将网页呈现为图像缓冲区并将其另存为指定文件.

目前,输出格式是根据文件扩展名自动设置的.支持的格式为PNG,JPEG和PDF.

它失败了,仅仅因为既不是/dev/fd/2也不/dev/stdout结束.PNG等等.

但没有恐惧,命名管道可以帮助你!

创建另一个命名管道,但这次使用扩展名.pdf:

mkfifo my_pipe.pdf
Run Code Online (Sandbox Code Playgroud)

现在,告诉它只是cat它的内容hn.pdf:

cat < my_pipe.pdf > hn.pdf &
Run Code Online (Sandbox Code Playgroud)

然后运行:

phantomjs render.coffee news.ycombinator.com my_pipe.pdf 
Run Code Online (Sandbox Code Playgroud)

看哪,美丽hn.pdf!

显然你想做一些更复杂的东西,只是cat输出,但我相信你现在应该做什么:)


TL; DR:

  1. 创建一个命名管道,使用".pdf"文件扩展名(所以它欺骗PhantomJS认为它是一个PDF文件):

    mkfifo my_pipe.pdf
    
    Run Code Online (Sandbox Code Playgroud)
  2. 做你想做的任何文件内容,如:

    cat < my_pipe.pdf > hn.pdf
    
    Run Code Online (Sandbox Code Playgroud)

    简直就是cat这样hn.pdf

  3. 在PhantomJS中,渲染到此文件/管道.

  4. 稍后,你应该删除管道:

    rm my_pipe.pdf
    
    Run Code Online (Sandbox Code Playgroud)


小智 14

正如Niko所指出的,您可以使用renderBase64()将网页呈现为图像缓冲区并将结果作为base64编码的字符串返回.
但目前这仅适用于PNG,JPEG和GIF.

要从phantomjs脚本写入stdout,只需使用文件系统API.

我使用这样的图像:

var base64image = page.renderBase64('PNG');
var fs = require("fs");
fs.write("/dev/stdout", base64image, "w");
Run Code Online (Sandbox Code Playgroud)

我不知道PDF格式是否renderBase64()会出现在phanthomjs的未来版本中,但作为一种解决方法,这些内容可能对您有用:

page.render(output);
var fs = require("fs");
var pdf = fs.read(output);
fs.write("/dev/stdout", pdf, "w");
fs.remove(output);
Run Code Online (Sandbox Code Playgroud)

outputpdf文件的路径在哪里.