phantom nodejs node generar example con pdf pipe file-descriptor phantomjs io-redirection

nodejs - phantom html to pdf node js



PhantomJS: PDF exportado a stdout (4)

¿Hay alguna manera de desencadenar la función de exportación de PDF en PhantomJS sin especificar un archivo de salida con la extensión .pdf? Nos gustaría usar stdout para generar el PDF.


Como señaló Niko, puede usar renderBase64() para representar la página web en un búfer de imagen y devolver el resultado como una cadena codificada en base64.
Pero por ahora esto solo funcionará para PNG, JPEG y GIF.

Para escribir algo desde una secuencia de comandos phantomjs a stdout simplemente use la API del sistema de archivos.

Utilizo algo como esto para las imágenes:

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

No sé si el formato PDF de renderBase64() estará en una versión futura de phanthomjs, pero como solución temporal, algo en este sentido puede funcionar para usted:

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

Donde la output es la ruta al archivo pdf.



Perdón por la respuesta extremadamente larga; Tengo la sensación de que necesitaré referirme a este método varias docenas de veces en mi vida, así que escribiré "una respuesta para gobernarlos a todos". Primero voy a balbucear un poco sobre los archivos, los descriptores de archivos, los conductos (con nombre) y la redirección de salida, y luego respondo tu pregunta.

Considere este sencillo programa 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; }

Muy sencillo. Toma un argumento (un nombre de archivo) e imprime un texto en él. No podría ser más simple.

Compilarlo con clang write_to_file.c -o write_to_file.o o gcc write_to_file.c -o write_to_file.o .

Ahora, ejecute ./write_to_file.o some_file (que se imprime en some_file ). Luego ejecuta cat some_file . El resultado, como se esperaba, es some text...

Ahora vamos a ser más elegantes. Escriba (./write_to_file.o /dev/stdout) > some_file en la terminal. Estamos pidiendo al programa que escriba en su salida estándar (en lugar de un archivo normal), y luego estamos redirigiendo ese stdout a some_file (usando > some_file ). Podríamos haber usado cualquiera de los siguientes para lograr esto:

  • (./write_to_file.o /dev/stdout) > some_file , que significa "use stdout "

  • (./write_to_file.o /dev/stderr) 2> some_file , que significa "usar stderr , y redirigirlo usando 2> "

  • (./write_to_file.o /dev/fd/2) 2> some_file , que es el mismo que el anterior; stderr es el tercer descriptor de archivo asignado a los procesos de Unix por defecto (después de stdin y stdout )

  • (./write_to_file.o /dev/fd/5) 5> some_file , que significa "usar el sexto descriptor de archivo y redirigirlo a some_file "

En caso de que no esté claro, estamos usando un conducto Unix en lugar de un archivo real (después de todo, todo es un archivo en Unix). Podemos hacer todo tipo de cosas sofisticadas con este tubo: escríbalo en un archivo, o escríbelo en una tubería con nombre y compártela entre diferentes procesos.

Ahora, creemos una tubería con nombre:

mkfifo my_pipe

Si escribe ls -l ahora, verá:

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

Tenga en cuenta la p al comienzo de la segunda línea. Significa que my_pipe es una tubería (con nombre).

Ahora, especifiquemos qué queremos hacer con nuestra tubería:

gzip -c < my_pipe > out.gz &

Significa: gzip lo que puse dentro de my_pipe y escribir los resultados en out.gz El & al final le pide al shell que ejecute este comando en segundo plano. Obtendrás algo como [1] 10449 y el control volverá a la terminal.

Luego, simplemente redirija la salida de nuestro programa C a esta tubería:

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

O

./write_to_file.o my_pipe

Obtendrás

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

lo que significa que el comando gzip ha terminado.

Ahora, haz otra 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

¡Hemos gzip éxito nuestro texto!

Ejecute gzip -d out.gz para descomprimir este archivo gzip ed. Se eliminará y se creará un nuevo archivo ( out ). cat out nos atrapa:

some text...

que es lo que esperábamos

¡No olvides quitar la tubería con rm my_pipe !

Ahora volvemos a PhantomJS.

Este es un simple script PhantomJS ( render.coffee , escrito en CoffeeScript) que toma dos argumentos: una URL y un nombre de archivo. Carga la URL, la procesa y la escribe en el nombre de archivo dado:

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()

Ahora escriba phantomjs render.coffee news.ycombinator.com hn.png en la terminal para phantomjs render.coffee news.ycombinator.com hn.png la página principal de Hacker News en el archivo hn.png . Funciona como se esperaba. Lo mismo ocurre con phantomjs render.coffee news.ycombinator.com hn.pdf .

Repitamos lo que hicimos antes con nuestro programa C:

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

No funciona ... :( ¿Por qué? Porque, como se indica en el manual de PhantomJS :

render (fileName)

Renderiza la página web a un buffer de imagen y la guarda como el archivo especificado.

Actualmente, el formato de salida se establece automáticamente en función de la extensión del archivo. Los formatos admitidos son PNG, JPEG y PDF.

Falla, simplemente porque ni /dev/fd/2 ni /dev/stdout terminan en .PNG , etc.

¡Pero sin miedo, las tuberías con nombre te pueden ayudar!

Cree otro conducto con nombre, pero esta vez use la extensión .pdf :

mkfifo my_pipe.pdf

Ahora, dígale que simplemente se lance a hn.pdf :

cat < my_pipe.pdf > hn.pdf &

Entonces corre:

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

¡Y he aquí el hermoso hn.pdf !

Obviamente, quieres hacer algo más sofisticado que simplemente obtener el resultado, pero estoy seguro de que ahora está claro lo que debes hacer :)

TL; DR:

  1. Cree un conducto con nombre, utilizando la extensión de archivo ".pdf" (por lo que engaña a PhantomJS para que piense que es un archivo PDF):

    mkfifo my_pipe.pdf

  2. Haga lo que quiera con el contenido del archivo, como:

    cat < my_pipe.pdf > hn.pdf

    que simplemente se lo da a hn.pdf

  3. En PhantomJS, preséntelo en este archivo / tubería.

  4. Más adelante, debes quitar la tubería:

    rm my_pipe.pdf


Puede enviar directamente a stdout sin necesidad de un archivo temporal.

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

Consulte aquí para obtener información sobre cuándo se agregó esto.

Si desea obtener HTML de stdin y enviar el PDF a stdout, consulte aquí