2010-05-07 6 views
30

Dopo 10 ore e provare altri 4 strumenti HTML in PDF, sono quasi pronto per esplodere.Come utilizzare wkhtmltopdf.exe in ASP.net

wkhtmltopdf suona come un'ottima soluzione ... il problema è che non riesco a eseguire un processo con abbastanza autorizzazioni da asp.net così ...

Process.Start("wkhtmltopdf.exe","http://www.google.com google.pdf"); 

partenze, ma non fa nulla.

c'è un modo semplice per uno:

-a) consentire asp.net per avviare processi (che può effettivamente fare qualcosa) o
-b) compilare/involucro/qualunque cosa wkhtmltopdf.exe in somthing I può utilizzare da C# come questo: WkHtmlToPdf.Save("http://www.google.com", "google.pdf");

+0

Questa non è una buona idea. IIS7 in modalità integrata vieta questo, inclusa la rappresentazione. –

risposta

18

Si potrebbe anche usare Pechkin

NET Wrapper per wkhtmltopdf DLL, libreria che utilizza il motore Webkit per pagine convertire HTML in PDF.

pacchetti Nuget:

Pechkin.Synchronized

Pechkin

+0

marcatura questo come accettato dopo tutti questi anni, perché ho dovuto usare wkhtmltopdf di nuovo su un nuovo progetto e Pechkin funzionato perfettamente! –

+0

Sto avendo problema con pechkin o Codaxy o addirittura WkhtmlXSharp, tutti non visualizzano i caratteri tailandesi (UTF-8) o font Unicode correttamente. Durante l'utilizzo iTextSharp e l'exe non dare quel problema – WickStargazer

+0

@DavidMurdoch stai avendo un qualsiasi file di blocco errori durante la distribuzione di un nuovo codice? se ho capito bene Pechkin utilizza una DLL nativa (wkhtmltopdf) e che dll nativa non potrebbe avere scaricato come le DLL gestite quando si caricare nuovi file? – Peter

4

Ecco il codice effettivo che ho usato. Sentitevi liberi di modificare questo per sbarazzarvi di alcuni odori e altri tipi di terrori ... so che non è fantastico.

using System; 
using System.Diagnostics; 
using System.IO; 
using System.Web; 
using System.Web.UI; 

public partial class utilities_getPDF : Page 
{ 
    protected void Page_Load(Object sender, EventArgs e) 
    { 
     string fileName = WKHtmlToPdf(myURL); 

     if (!string.IsNullOrEmpty(fileName)) 
     { 
      string file = Server.MapPath("~\\utilities\\GeneratedPDFs\\" + fileName); 
      if (File.Exists(file)) 
      { 
       var openFile = File.OpenRead(file); 
       // copy the stream (thanks to http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances-c) 
       byte[] buffer = new byte[32768]; 
       while (true) 
       { 
        int read = openFile.Read(buffer, 0, buffer.Length); 
        if (read <= 0) 
        { 
         break; 
        } 
        Response.OutputStream.Write(buffer, 0, read); 
       } 
       openFile.Close(); 
       openFile.Dispose(); 

       File.Delete(file); 
      } 
     } 
    } 

    public string WKHtmlToPdf(string Url) 
    { 
     var p = new Process(); 

     string switches = ""; 
     switches += "--print-media-type "; 
     switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm "; 
     switches += "--page-size Letter "; 
     // waits for a javascript redirect it there is one 
     switches += "--redirect-delay 100"; 

     // Utils.GenerateGloballyUniuqueFileName takes the extension from 
     // basically returns a filename and prepends a GUID to it (and checks for some other stuff too) 
     string fileName = Utils.GenerateGloballyUniqueFileName("pdf.pdf"); 

     var startInfo = new ProcessStartInfo 
         { 
          FileName = Server.MapPath("~\\utilities\\PDF\\wkhtmltopdf.exe"), 
          Arguments = switches + " " + Url + " \"" + 
             "../GeneratedPDFs/" + fileName 
             + "\"", 
          UseShellExecute = false, // needs to be false in order to redirect output 
          RedirectStandardOutput = true, 
          RedirectStandardError = true, 
          RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none 
          WorkingDirectory = Server.MapPath("~\\utilities\\PDF") 
         }; 
     p.StartInfo = startInfo; 
     p.Start(); 

     // doesn't work correctly... 
     // read the output here... 
     // string output = p.StandardOutput.ReadToEnd(); 

     // wait n milliseconds for exit (as after exit, it can't read the output) 
     p.WaitForExit(60000); 

     // read the exit code, close process 
     int returnCode = p.ExitCode; 
     p.Close(); 

     // if 0, it worked 
     return (returnCode == 0) ? fileName : null; 
    } 
} 
+3

Non è "se 0 o 2, ha funzionato". Funziona solo quando 0. Altri valori: 1: (?) O 8 generico valore del codice di fallimento della exit_error. 2: Errore 404, non trovato (e PDF vuoto). 3: Errore 401, non autorizzato. Come da specifica unix, qualsiasi valore diverso da uno 0 restituito da un processo segnala una sorta di errore. Questo vale anche per i programmi Windows. – Christian

+0

grazie, +1 per la correzione. Il codice (e i commenti) sono originati da http://stackoverflow.com/questions/1331926/asp-net-calling-exe/1698839#1698839 –

+0

Ho provato sia la versione 0.99 che la versione più recente di RC e nessuno di loro sembra supporto --redirect-delay - qualche idea? Che versione stavi usando? Mi sta facendo impazzire! Funziona perfettamente a parte non aspettare le mie chiamate ajax per arrivare. –

22

ho appena iniziato un nuovo progetto per fornire un C# P/Invoke wrapper wkhtmltopdf.

Puoi esaminare il mio codice a: https://github.com/pruiz/WkHtmlToXSharp

saluta.

+0

wow, sembra piuttosto lucido. Grazie per la condivisione! –

+3

Ottengo errori quando utilizzo MultiplexingConverter come "Tentativo di leggere o scrivere memoria protetta." Questa è spesso un'indicazione che un'altra memoria è corrotta. " C'è un modo per prevenire questo errore? – Brennan

+1

Non ho le mie immagini incorporate nel pdf. Nulla mostra dove deve essere visualizzata l'immagine. Ho verificato se si è verificato a causa dell'URL relativo dell'immagine e convertito in quelli assoluti. Ancora nessun successo. Cosa funzionerebbe? – sangam

12

Grazie a Paul, ho trovato il buono wrapper scritto da Codaxy, che può anche essere facilmente scaricato tramite NuGet.

Dopo un paio di prove, sono riuscito questa azione MVC, che crea istantaneamente e restituisce il file PDF come un flusso:

public ActionResult Pdf(string url, string filename) 
{ 
    MemoryStream memory = new MemoryStream(); 
    PdfDocument document = new PdfDocument() { Url = url }; 
    PdfOutput output = new PdfOutput() { OutputStream = memory }; 

    PdfConvert.ConvertHtmlToPdf(document, output); 
    memory.Position = 0; 

    return File(memory, "application/pdf", Server.UrlEncode(filename)); 
} 

Qui, il PDF * classi sono state implementate nella confezione, con un bel codice pulito, purtroppo privo di documentazione.

All'interno del convertitore, l'URL verrà convertito in PDF, archiviato in un file temporaneo, copiato nello stream che abbiamo assegnato come parametro e in seguito il file PDF verrà eliminato.

Infine, dobbiamo spostare lo stream come FileStreamResult.

Non dimenticare di impostare la posizione del flusso di output su zero, altrimenti i file PDF verranno scaricati come zero byte di dimensioni.

+0

Per generare stream PDF tramite MVC ActionResult direttamente sul browser web, @ endy-tjahjono ha dimostrato un buon approccio tramite [qui] (http://stackoverflow.com/questions/6168846/open-pdf-result-in-browser-tab-with-mvc-3) –

+0

Sta funzionando bene ma si blocca quando provo a usare un'intestazione (che funziona normalmente usando direttamente lo strumento della riga di comando). – marquito

+0

@marquito: intendi il tag "header" in HTML5? Non ho alcuna esperienza, ma hai provato a sostituirlo con un buon vecchio amico "div"? –