programing

HTML에서 PDF를 생성하기 위해 wkhtmltopdf 호출

starjava 2023. 9. 9. 08:50
반응형

HTML에서 PDF를 생성하기 위해 wkhtmltopdf 호출

HTML 파일로 PDF 파일을 만들려고 합니다.주위를 조금 둘러본 후에 나는 발견했다: wkhtmltopdf는 완벽합니다.ASP에서 이 .exe를 불러야 합니다.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 파일을 동적으로 만들 수 있는 더 좋은 솔루션이 있으면 알려주세요.

삭제:
아래 제 답변은 디스크에 pdf 파일을 생성합니다.그런 다음 해당 파일을 다운로드로 사용자 브라우저에 스트리밍했습니다.아래의 Hath의 답변과 같은 것을 사용하여 wkhtml2pdf를 스트림으로 출력한 후 사용자에게 직접 전송하는 것을 고려해 보십시오. 이는 파일 권한 등과 관련된 많은 문제를 우회할 것입니다.

대답 :
ASP에서 쓸 수 있는 PDF의 출력 경로를 지정했는지 확인합니다.서버에서 실행되는 IIS의 NET 프로세스(일반적으로 NETWORK_SERVICE).

내 것은 다음과 같습니다. (그리고 작동합니다.)

/// <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);
}

윈도우 서비스로 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;
}

Graham Ambrose와 다른 모든 분들께 감사드립니다.

좋아요, 오래된 질문이지만 아주 좋은 질문입니다.그리고 좋은 답을 찾지 못해서 직접 만들었습니다 :) 그리고 아주 간단한 프로젝트를 GitHub에 올렸습니다.

샘플 코드는 다음과 같습니다.

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

다음은 몇 가지 핵심 사항입니다.

  • P/Invoke 없음
  • 새 프로세스를 생성하지 않음
  • 파일 시스템 없음(모두 RAM에 있음)
  • 네이티브.지능형 등을 갖춘 NET DLL
  • 또는 (PDF PNG ( )HtmlToXConverter.ConvertToPng)

wkhtmltopdf 라이브러리에 대한 C# 래퍼 라이브러리(P/Invoke 사용)를 확인하십시오. https://github.com/pruiz/WkHtmlToXSharp

이것이 일반적으로 나쁜 생각인 이유는 여러 가지가 있습니다.충돌이 발생할 경우 생성되지만 메모리에 저장되는 실행 파일을 어떻게 제어할 것입니까?서비스 거부 공격이나 악의적인 것이 TestPDF.htm에 들어가면 어떻게 됩니까?

제가 알기로는 ASP입니다.NET 사용자 계정에는 로컬로 로그온할 수 있는 권한이 없습니다.또한 실행 파일에 액세스하고 파일 시스템에 쓰기 위한 올바른 파일 권한이 있어야 합니다.로컬 보안 정책을 편집하고 ASP를 허용해야 합니다.NET 사용자 계정(ASPNET일 수 있음)이 로컬로 로그온합니다(기본적으로 거부 목록에 있을 수 있음).그런 다음 다른 파일에 대한 NTFS 파일 시스템의 권한을 편집해야 합니다.공유 호스팅 환경에 있는 경우 필요한 구성을 적용할 수 없습니다.

이와 같은 외부 실행 파일을 사용하는 가장 좋은 방법은 ASP에서 작업을 대기열에 넣는 것입니다.NET 코드를 코드화하고 일종의 서비스가 대기열을 모니터링하도록 합니다.이렇게 하면 온갖 나쁜 일들이 일어나는 것으로부터 자신을 보호할 수 있을 것입니다.사용자 계정을 변경할 때 발생하는 유지보수 문제는 노력할 가치가 없다고 생각하며, 서비스나 예정된 작업을 설정하는 것은 어려움이지만 더 나은 설계일 뿐입니다.ASP.NET 페이지는 출력에 대한 결과 큐를 폴링해야 하며, 사용자에게 대기 페이지를 제공할 수 있습니다.이는 대부분의 경우 허용됩니다.

출력 파일로 "-"를 지정하여 출력을 sout로 전송하도록 wkhtmltopdf에 지시할 수 있습니다.그러면 프로세스의 출력을 응답 스트림으로 읽을 수 있고 파일 시스템에 쓸 때 발생하는 권한 문제를 방지할 수 있습니다.

2018년에 대한 제 의견입니다.

저는 비동기를 사용하고 있습니다.저는 wkhtmltopdf 로 스트리밍하고 있습니다.wkhtmltopdf가 기본적으로 utf-8을 예상하고 있기 때문에 새로운 StreamWriter를 만들었지만 프로세스가 시작되면 다른 것으로 설정됩니다.

저는 사용자마다 주장이 다르기 때문에 많은 주장을 포함하지 않았습니다.추가 Args를 사용하여 필요한 것을 추가할 수 있습니다.

p를 제거했습니다.하지 않았고 WaitForExit(...)에 것이기 때문에 WaitForExit(...)이 실패하면 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();
    }
}

내가 거기에 포함하지 않았지만 htmltopdf가 html 페이지를 렌더링할 때 로드해야 하는 이미지, CSS 또는 다른 것이 있다면 유용할 수 있습니다.

  • --cookie를 사용하여 인증 쿠키를 전달할 수 있습니다.
  • html 페이지의 헤더에서, 당신은 서버를 가리키는 href로 기본 태그를 설정할 수 있고 wkhtmltopdf는 필요하다면 그것을 사용할 것입니다.

질문/답변/위의 모든 의견에 감사드립니다.이것은 제가 PDF에 WKHTML을 위한 C# 포장지를 작성할 때 알게 되었고, 제가 가진 몇 가지 문제점에 대해 답변해 주었습니다.저는 이에 대해 블로그 포스트에 글을 올렸는데, 여기에는 제 포장지도 포함되어 있습니다. (위의 항목에서 나온 "영감"이 제 코드에 스며드는 것을 보실 수 있을 것입니다.)

WKHTML을 사용하여 C#의 HTML로 PDF 만들기

다시 한번 감사드립니다 여러분!

ASP.넷프로세스는 아마도 디렉토리에 대한 쓰기 권한이 없을 것입니다.

편지를 쓰라고 해보세요.%TEMP%, 효과가 있는지 확인해 보세요

그리고 ASP를 만드세요.넷 페이지는 프로세스의 stdout과 stderr을 에코하고 오류 메시지를 확인합니다.

일반적으로 pdf 파일이 제대로 올바르게 생성되면 반환 코드 =0이 나타납니다.생성되지 않은 경우 값은 -ve 범위입니다.

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

언급URL : https://stackoverflow.com/questions/1331926/calling-wkhtmltopdf-to-generate-pdf-from-html

반응형