54

HTMLファイルからPDFファイルを作成しようとしています。少し調べてみたところ、wkhtmltopdfが完璧であることがわかりました。この .exe を ASP.NET サーバーから呼び出す必要があります。私が試みた:

    Process p = new Process();
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.FileName = HttpContext.Current.Server.MapPath("wkhtmltopdf.exe");
    p.StartInfo.Arguments = "TestPDF.htm TestPDF.pdf";
    p.Start();
    p.WaitForExit();

サーバー上にファイルが作成されても成功しません。誰でも正しい方向へのポインタを教えてもらえますか? サイトの最上位ディレクトリに wkhtmltopdf.exe ファイルを置きます。他に開催すべき場所はありますか?


編集: html から動的に pdf ファイルを作成するためのより良い解決策がある場合は、お知らせください。

4

11 に答える 11

51

更新:
以下の私の答えは、ディスク上にpdfファイルを作成します。次に、そのファイルをダウンロードとしてユーザーのブラウザーにストリーミングしました。以下のHathの回答のようなものを使用して、代わりにwkhtml2pdfをストリームに出力し、それをユーザーに直接送信することを検討してください。これにより、ファイルのアクセス許可などに関する多くの問題が回避されます.

私の最初の答え:
サーバーで実行されているIISのASP.NETプロセス(通常はNETWORK_SERVICEだと思います)によって書き込み可能なPDFの出力パスを指定したことを確認してください。

私は次のようになります(そして動作します):

/// <summary>
/// Convert Html page at a given URL to a PDF file using open-source tool wkhtml2pdf
/// </summary>
/// <param name="Url"></param>
/// <param name="outputFilename"></param>
/// <returns></returns>
public static bool HtmlToPdf(string Url, string outputFilename)
{
    // assemble destination PDF file name
    string filename = ConfigurationManager.AppSettings["ExportFilePath"] + "\\" + outputFilename + ".pdf";

    // get proj no for header
    Project project = new Project(int.Parse(outputFilename));

    var p = new System.Diagnostics.Process();
    p.StartInfo.FileName = ConfigurationManager.AppSettings["HtmlToPdfExePath"];

    string switches = "--print-media-type ";
    switches += "--margin-top 4mm --margin-bottom 4mm --margin-right 0mm --margin-left 0mm ";
    switches += "--page-size A4 ";
    switches += "--no-background ";
    switches += "--redirect-delay 100";

    p.StartInfo.Arguments = switches + " " + Url + " " + filename;

    p.StartInfo.UseShellExecute = false; // needs to be false in order to redirect output
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    p.StartInfo.RedirectStandardInput = true; // redirect all 3, as it should be all 3 or none
    p.StartInfo.WorkingDirectory = StripFilenameFromFullPath(p.StartInfo.FileName);

    p.Start();

    // read the output here...
    string output = p.StandardOutput.ReadToEnd(); 

    // ...then 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 or 2, it worked (not sure about other values, I want a better way to confirm this)
    return (returnCode == 0 || returnCode == 2);
}
于 2009-11-09T02:43:38.477 に答える
41

Windows サービスで msmq を使用しようとしたときに同じ問題が発生しましたが、何らかの理由で非常に遅くなりました。(プロセス部分)。

これが最終的に機能したものです:

private void DoDownload()
{
    var url = Request.Url.GetLeftPart(UriPartial.Authority) + "/CPCDownload.aspx?IsPDF=False?UserID=" + this.CurrentUser.UserID.ToString();
    var file = WKHtmlToPdf(url);
    if (file != null)
    {
        Response.ContentType = "Application/pdf";
        Response.BinaryWrite(file);
        Response.End();
    }
}

public byte[] WKHtmlToPdf(string url)
{
    var fileName = " - ";
    var wkhtmlDir = "C:\\Program Files\\wkhtmltopdf\\";
    var wkhtml = "C:\\Program Files\\wkhtmltopdf\\wkhtmltopdf.exe";
    var p = new Process();

    p.StartInfo.CreateNoWindow = true;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    p.StartInfo.RedirectStandardInput = true;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.FileName = wkhtml;
    p.StartInfo.WorkingDirectory = wkhtmlDir;

    string switches = "";
    switches += "--print-media-type ";
    switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm ";
    switches += "--page-size Letter ";
    p.StartInfo.Arguments = switches + " " + url + " " + fileName;
    p.Start();

    //read output
    byte[] buffer = new byte[32768];
    byte[] file;
    using(var ms = new MemoryStream())
    {
        while(true)
        {
            int read =  p.StandardOutput.BaseStream.Read(buffer, 0,buffer.Length);

            if(read <=0)
            {
                break;
            }
            ms.Write(buffer, 0, read);
        }
        file = ms.ToArray();
    }

    // wait or exit
    p.WaitForExit(60000);

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

    return returnCode == 0 ? file : null;
}

グラハム・アンブローズと他のみんなに感謝します。

于 2010-09-10T10:07:56.763 に答える
7

wkhtmltopdfライブラリのC#ラッパーライブラリ(P / Invokeを使用)を確認してください:https ://github.com/pruiz/WkHtmlToXSharp

于 2011-04-04T23:07:26.350 に答える
5

出力ファイルとして「-」を指定することで、wkhtmltopdf にその出力を sout に送信するように指示できます。その後、プロセスからの出力を応答ストリームに読み取り、ファイル システムへの書き込みに関する権限の問題を回避できます。

于 2010-03-03T21:52:21.880 に答える
5

これが一般的に悪い考えである理由はたくさんあります。クラッシュが発生した場合に生成された実行可能ファイルをどのように制御しますか? DoS 攻撃や、悪意のあるものが TestPDF.htm に侵入した場合はどうなりますか?

私の理解では、ASP.NET ユーザー アカウントにはローカルでログオンする権限がありません。また、実行可能ファイルにアクセスしてファイル システムに書き込むには、適切なファイル アクセス許可が必要です。ローカル セキュリティ ポリシーを編集し、ASP.NET ユーザー アカウント (おそらく ASPNET) がローカルでログオンできるようにする必要があります (デフォルトで拒否リストに含まれている可能性があります)。次に、他のファイルの NTFS ファイル システムのアクセス許可を編集する必要があります。共有ホスティング環境にいる場合、必要な構成を適用できない場合があります。

このような外部実行可能ファイルを使用する最善の方法は、ASP.NET コードからジョブをキューに入れ、何らかのサービスでキューを監視することです。これを行うと、あらゆる種類の悪いことが起こるのを防ぐことができます。ユーザー アカウントの変更に伴うメンテナンスの問題は、私の意見では努力する価値がありません。サービスやスケジュールされたジョブを設定するのは面倒ですが、より良い設計です。ASP.NET ページは出力の結果キューをポーリングする必要があり、待機ページをユーザーに提示できます。これはほとんどの場合許容されます。

于 2009-08-26T01:47:41.133 に答える
3

2018年のものでこれを取り上げます。

私は非同期を使用しています。wkhtmltopdf との間でストリーミングしています。wkhtmltopdf はデフォルトで utf-8 を想定しているため、新しい StreamWriter を作成しましたが、プロセスの開始時に別の値に設定されています。

引数はユーザーごとに異なるため、多くの引数は含めませんでした。additionalArgs を使用して必要なものを追加できます。

p.WaitForExit(...) を削除したのは、失敗した場合に処理していなかったため、いずれにせよハングするからですawait tStandardOutput。タイムアウトが必要な場合はWait(...)、キャンセルトークンまたはタイムアウトを使用してさまざまなタスクを呼び出し、それに応じて処理する必要があります。

public async Task<byte[]> GeneratePdf(string html, string additionalArgs)
{
    ProcessStartInfo psi = new ProcessStartInfo
    {
        FileName = @"C:\Program Files\wkhtmltopdf\wkhtmltopdf.exe",
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        Arguments = "-q -n " + additionalArgs + " - -";
    };

    using (var p = Process.Start(psi))
    using (var pdfSream = new MemoryStream())
    using (var utf8Writer = new StreamWriter(p.StandardInput.BaseStream, 
                                             Encoding.UTF8))
    {
        await utf8Writer.WriteAsync(html);
        utf8Writer.Close();
        var tStdOut = p.StandardOutput.BaseStream.CopyToAsync(pdfSream);
        var tStdError = p.StandardError.ReadToEndAsync();

        await tStandardOutput;
        string errors = await tStandardError;

        if (!string.IsNullOrEmpty(errors)) { /* deal/log with errors */ }

        return pdfSream.ToArray();
    }
}

そこには含まれていませんが、HTMLページをレンダリングするときにwkhtmltopdfがロードする必要がある画像、css、またはその他のものがある場合に役立ちます。

  • --cookie を使用して認証 Cookie を渡すことができます
  • HTMLページのヘッダーで、サーバーを指すhrefを使用してベースタグを設定すると、必要に応じてwkhtmltopdfがそれを使用します
于 2018-11-27T21:54:24.910 に答える
2

上記の質問/回答/すべてのコメントをありがとう。私がWKHTMLtoPDF用の独自のC#ラッパーを書いているときにこれに遭遇し、それは私が抱えていたいくつかの問題に答えました。私はこれについてブログ投稿に書くことになりました-これには私のラッパーも含まれています(上記のエントリからの「インスピレーション」が私のコードに浸透しているのは間違いありません...)

WKHTMLtoPDFを使用してC#でHTMLからPDFを作成する

みんなありがとう!

于 2012-04-05T07:52:54.893 に答える
0

通常、PDFファイルが適切かつ正しく作成されている場合はリターンコード= 0が返されます。作成されていない場合、値は-veの範囲内にあります。

于 2011-12-17T06:33:35.247 に答える
0

ASP .Net プロセスには、おそらくディレクトリへの書き込みアクセス権がありません。

に書き込むように指示してみて%TEMP%、動作するかどうかを確認してください。

また、ASP .Net ページがプロセスの stdout と stderr をエコーするようにし、エラー メッセージをチェックします。

于 2009-08-26T01:47:13.210 に答える
-1
using System;
using System.Diagnostics;
using System.Web;

public partial class pdftest : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    private void fn_test()
    {
        try
        {
            string url = HttpContext.Current.Request.Url.AbsoluteUri;
            Response.Write(url);
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = 
                @"C:\PROGRA~1\WKHTML~1\wkhtmltopdf.exe";//"wkhtmltopdf.exe";
            startInfo.Arguments = url + @" C:\test"
                 + Guid.NewGuid().ToString() + ".pdf";
            Process.Start(startInfo);
        }
        catch (Exception ex)
        {
            string xx = ex.Message.ToString();
            Response.Write("<br>" + xx);
        }
    }
    protected void btn_test_Click(object sender, EventArgs e)
    {
        fn_test();
    }
}
于 2010-02-06T16:25:35.013 に答える