【问题标题】:PhantomJS: exported PDF to stdoutPhantomJS:将 PDF 导出到标准输出
【发布时间】:2012-07-09 15:15:01
【问题描述】:

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

【问题讨论】:

    标签: pdf pipe file-descriptor phantomjs io-redirection


    【解决方案1】:

    抱歉,答案太长了;我有一种感觉,我一生中需要参考这种方法几十次,所以我会写“一个答案来统治他们”。我会先唠叨一下文件、文件描述符、(命名的)管道和输出重定向,然后回答你的问题。


    考虑这个简单的 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;
    }
    

    非常简单。它接受一个参数(文件名)并将一些文本打印到其中。再简单不过了。


    clang write_to_file.c -o write_to_file.ogcc 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) &gt; some_file。我们要求程序写入其标准输出(而不是常规文件),然后我们将stdout 重定向到some_file(使用&gt; some_file)。我们可以使用以下任何方法来实现这一点:

    • (./write_to_file.o /dev/stdout) &gt; some_file,意思是“使用stdout

    • (./write_to_file.o /dev/stderr) 2&gt; some_file,表示“使用stderr,并使用2&gt;重定向”

    • (./write_to_file.o /dev/fd/2) 2&gt; some_file,同上; stderr 是默认分配给 Unix 进程的第三个文件描述符(在 stdinstdout 之后)

    • (./write_to_file.o /dev/fd/5) 5&gt; some_file,意思是“使用你的第六个文件描述符,并将其重定向到some_file

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


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

    mkfifo my_pipe
    

    如果你现在输入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
    

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

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

    gzip -c < my_pipe > out.gz &
    

    它的意思是:gzip 我在my_pipe 中输入的内容,并将结果写入out.gz。最后的&amp; 要求shell 在后台运行这个命令。您会得到类似[1] 10449 的信息,并且控件会返回到终端。

    然后,简单地将我们的 C 程序的输出重定向到这个管道:

    (./write_to_file.o /dev/fd/5) 5> my_pipe
    

    或者

    ./write_to_file.o my_pipe
    

    你会得到

    [1]+  Done                    gzip -c < my_pipe > out.gz
    

    这意味着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
    

    我们已经成功gziped 我们的文字!

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

    some text...
    

    这是我们所期望的。

    别忘了用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()
    

    现在在终端中输入phantomjs render.coffee news.ycombinator.com hn.png,将黑客新闻首页呈现到文件hn.png中。它按预期工作。 phantomjs render.coffee news.ycombinator.com hn.pdf 也是如此。

    让我们重复之前对 C 程序所做的操作:

    (phantomjs render.coffee news.ycombinator.com /dev/fd/5) 5> hn.pdf
    

    它不起作用... :( 为什么?因为,正如 PhantomJS's manual 所述:

    渲染(文件名)

    将网页渲染到图像缓冲区并保存 作为指定文件。

    目前输出格式是根据文件自动设置的 延期。支持的格式为 PNG、JPEG 和 PDF。

    它失败了,仅仅是因为/dev/fd/2/dev/stdout 都没有以.PNG 结尾,等等。

    但不用担心,命名管道可以帮助您!

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

    mkfifo my_pipe.pdf
    

    现在,告诉它简单地将cat 其输入输出到hn.pdf

    cat < my_pipe.pdf > hn.pdf &
    

    然后运行:

    phantomjs render.coffee news.ycombinator.com my_pipe.pdf 
    

    看看美丽的hn.pdf

    显然你想做一些比cat输出更复杂的事情,但我相信现在你应该做什么很清楚:)


    TL;DR:

    1. 创建一个命名管道,使用“.pdf”文件扩展名(所以它会让 PhantomJS 误以为它是一个 PDF 文件):

      mkfifo my_pipe.pdf
      
    2. 对文件的内容做任何你想做的事情,比如:

      cat < my_pipe.pdf > hn.pdf
      

      简单地将cats 发送到hn.pdf

    3. 在 PhantomJS 中,渲染到这个文件/管道。

    4. 稍后,您应该移除管道:

      rm my_pipe.pdf
      

    【讨论】:

      【解决方案2】:

      我不知道它是否能解决您的问题,但您也可以查看添加到 PhantomJS 1.6 中的新 renderBase64() 方法:https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L623

      很遗憾,该功能尚未在 wiki 上记录:/

      【讨论】:

        【解决方案3】:

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

        要将 phantomjs 脚本中的内容写入标准输出,只需使用文件系统 API。

        我对图像使用这样的东西:

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

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

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

        output 是 pdf 文件的路径。

        【讨论】:

        • 为了让这个方法起作用,我不得不将fs.read 行更改为var pdf = fs.open(output, 'rb').read();——以二进制形式读取文件很重要(否则将标准输出重定向到文件会导致不正确的PDF)。然而,我后来能够让它与 no临时文件 一起工作 - 请参阅 stackoverflow.com/a/17282463/137067
        【解决方案4】:

        您可以直接输出到标准输出,无需临时文件。

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

        查看here 了解添加时间的历史记录。

        如果您想从标准输入获取 HTML 并将 PDF 输出到标准输出,see here

        【讨论】:

        • 工作得很好,但是如果您正在读取 Node 中的标准输出,例如此处 npmjs.org/package/phantomjs 中所述,您需要设置二进制的 execFile 选项,并且可能会增加缓冲区大小,如此处所述 stackoverflow.com/a/6170723/2297380
        • 这里是documentation from the PhantomJS wiki。但对我来说,这种方法不尊重page.viewportSize 设置。
        • 出于某种原因,它可以在 Mac OS X 上运行,但不能在 linux 上运行(PhantomJS 版本 1.9.8)。
        • 即使添加选项:{encoding: 'binary', maxBuffer: 5000*1024} 这不会到达节点中的标准输出
        • 对我来说在 Windows 7 和 phantomjs-prebuilt 2.1.13 中无法使用
        猜你喜欢
        • 2013-10-31
        • 2013-05-01
        • 2013-04-11
        • 1970-01-01
        • 2012-08-22
        • 2018-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多