2011-01-10 10 views
22

wie html als string anstelle von url in wkhtmltopdf mit asp.net, C# übergeben?Wie HTML als String mit wkhtmltopdf übergeben?

+0

Es wäre wahrscheinlich am einfachsten, stdin umzuleiten und die Zeichenfolge auf diese Weise zu übergeben. Sie könnten den Text auch in eine Datei schreiben (obwohl das natürlich nicht sehr gut skalierbar ist). –

+0

Können Sie bitte ein Beispiel zeigen? – Eugene

Antwort

16

Umleiten STDIN ist wahrscheinlich der einfachste Weg, um zu erreichen, was Sie versuchen zu tun.

Ein Verfahren zur STDIN mit wkhtmltopdf Umleitung (in ASP.Net) ist wie folgt:

private void WritePDF(string HTML) 
    { 
     string inFileName, 
       outFileName, 
       tempPath; 
     Process p; 
     System.IO.StreamWriter stdin; 
     ProcessStartInfo psi = new ProcessStartInfo(); 

     tempPath = Request.PhysicalApplicationPath + "temp\\"; 
     inFileName = Session.SessionID + ".htm"; 
     outFileName = Session.SessionID + ".pdf"; 

     // run the conversion utility 
     psi.UseShellExecute = false; 
     psi.FileName = "c:\\Program Files (x86)\\wkhtmltopdf\\wkhtmltopdf.exe"; 
     psi.CreateNoWindow = true; 
     psi.RedirectStandardInput = true; 
     psi.RedirectStandardOutput = true; 
     psi.RedirectStandardError = true; 

     // note that we tell wkhtmltopdf to be quiet and not run scripts 
     // NOTE: I couldn't figure out a way to get both stdin and stdout redirected so we have to write to a file and then clean up afterwards 
     psi.Arguments = "-q -n - " + tempPath + outFileName; 

     p = Process.Start(psi); 

     try 
     { 
      stdin = p.StandardInput; 
      stdin.AutoFlush = true; 

      stdin.Write(HTML); 
      stdin.Close(); 

      if (p.WaitForExit(15000)) 
      { 
       // NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works 
       Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName)); 
       //Response.WriteFile(tempPath + outFileName); 
      } 
     } 
     finally 
     { 
      p.Close(); 
      p.Dispose(); 
     } 

     // delete the pdf 
     System.IO.File.Delete(tempPath + outFileName); 
    } 

Beachten Sie, dass der obige Code wird davon ausgegangen, dass es ein Temp Verzeichnis in Ihrem Anwendungsverzeichnis. Außerdem müssen Sie den Schreibzugriff auf dieses Verzeichnis für das Benutzerkonto explizit aktivieren, das beim Ausführen des IIS-Prozesses verwendet wird.

+0

Ich kann das nicht mit referenzierten Bildern und Stylesheets arbeiten. So möglich? Wenn ich die Befehlszeile mit Dateien auf der Festplatte ausführe, funktioniert es. – smerlung

+0

@smerlung Stellen Sie sicher, dass die Webanwendung Zugriff auf die Dateien hat, auf die Sie verweisen. Wenn Sie über die Befehlszeile ausgeführt werden, führen Sie Ihre Anmeldung aus. Wenn Sie über den Webserver laufen, laufen Sie als Webserver-Login, der möglicherweise keinen Zugriff auf die von Ihnen verwendeten Dateien hat. Stellen Sie außerdem sicher, dass der Pfad zu den Dateien auch von diesem Speicherort aus sinnvoll ist. Wenn Sie zum Beispiel auf '.. \ styles \ style.css' verweisen, dann müssen Sie ein 'styles'-Verzeichnis' oberhalb ', in dem Sie das Programm ausführen, das die Datei' style.css' enthält. –

+0

Ich habe es funktioniert mit absoluten Pfaden. Aber das ist nicht sehr nett. Ich verstehe, was du sagst, aber mein Problem ist, dass ich nicht verstehe, wo der Prozess "läuft". Ich habe verschiedene Optionen für WorkingDirectory ausprobiert, konnte es aber nicht herausfinden? – smerlung

37

STDIn und STDOut wurden in diesem Beispiel umgeleitet, so dass Sie überhaupt keine Dateien benötigen sollten.

public static class Printer 
{ 
    public const string HtmlToPdfExePath = "wkhtmltopdf.exe"; 

    public static bool GeneratePdf(string commandLocation, StreamReader html, Stream pdf, Size pageSize) 
    { 
     Process p; 
     StreamWriter stdin; 
     ProcessStartInfo psi = new ProcessStartInfo(); 

     psi.FileName = Path.Combine(commandLocation, HtmlToPdfExePath); 
     psi.WorkingDirectory = Path.GetDirectoryName(psi.FileName); 

     // run the conversion utility 
     psi.UseShellExecute = false; 
     psi.CreateNoWindow = true; 
     psi.RedirectStandardInput = true; 
     psi.RedirectStandardOutput = true; 
     psi.RedirectStandardError = true; 

     // note: that we tell wkhtmltopdf to be quiet and not run scripts 
     psi.Arguments = "-q -n --disable-smart-shrinking " + (pageSize.IsEmpty? "" : "--page-width " + pageSize.Width + "mm --page-height " + pageSize.Height + "mm") + " - -"; 

     p = Process.Start(psi); 

     try { 
      stdin = p.StandardInput; 
      stdin.AutoFlush = true; 
      stdin.Write(html.ReadToEnd()); 
      stdin.Dispose(); 

      CopyStream(p.StandardOutput.BaseStream, pdf); 
      p.StandardOutput.Close(); 
      pdf.Position = 0; 

      p.WaitForExit(10000); 

      return true; 
     } catch { 
      return false; 

     } finally { 
      p.Dispose(); 
     } 
    } 

    public static void CopyStream(Stream input, Stream output) 
    { 
     byte[] buffer = new byte[32768]; 
     int read; 
     while ((read = input.Read(buffer, 0, buffer.Length)) > 0) { 
      output.Write(buffer, 0, read); 
     } 
    } 
} 
+0

keine Notwendigkeit, ** Schließen ** und ** Dispose ** den Prozess, es tut das gleiche. Entsorgen Sie es einfach oder legen Sie es in ein benutzendes Konstrukt. Gleiches gilt für Streams. –

+0

Jetzt nur entsorgen heißt –

+0

ja das ist besser :) –

3

Ich weiß, dies ist ein älterer Beitrag, aber ich möchte zukünftige Entwickler diese Option haben. Ich hatte das gleiche Bedürfnis, und die Idee, einen Hintergrundprozess zu starten, nur um ein PDF in einer Web-App zu bekommen, ist schrecklich.

Hier ist eine andere Option: https://github.com/TimothyKhouri/WkHtmlToXDotNet

Es ist eine .NET nativen Wrapper um wkhtmltopdf.

Beispielcode hier:

var pdfData = HtmlToXConverter.ConvertToPdf("<h1>COOOL!</h1>"); 

Hinweis, es ist nicht Thread-sicher ab jetzt - ich bin daran arbeiten. Also benutze einfach einen Monitor oder etwas oder ein Schloss.

Verwandte Themen