【问题标题】:wkhtmltopdf outputstream & download - diaglogwkhtmltopdf 输出流和下载 - 对话
【发布时间】:2018-11-28 23:44:57
【问题描述】:

是否可以从任何 html 文件中获取由 wkhtmltopdf 创建的 pdf 流并在 IE/Firefox/Chrome 等中弹出下载对话框?

目前我通过这段代码获得了输出流:

public class Printer
{
    public static MemoryStream GeneratePdf(StreamReader Html, MemoryStream pdf, Size pageSize)
    {
        Process p;
        StreamWriter stdin;
        ProcessStartInfo psi = new ProcessStartInfo();

        psi.FileName =  @"C:\PROGRA~1\WKHTML~1\wkhtmltopdf.exe";

        // 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 pdf;
        }
        catch
        {
            return null;
        }
        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);
        }
    }
}

那我要显示对话框:

MemoryStream PDF = Printer.GeneratePdf(Rd, PDFStream, Size);

byte[] byteArray1 = PDF.ToArray();
PDF.Flush();
PDF.Close();
Response.BufferOutput = true;

Response.Clear();
Response.ClearHeaders();
Response.AddHeader("Content-Disposition", "attachment; filename=Test.pdf");
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(byteArray1);
Response.End();

使用从 PDF 文件创建的 MemoryStreams 可以正常工作,但在这里我只得到一个空页面。 bytearray 有 1270 个字节。

【问题讨论】:

  • 您解决了这个问题吗?我的解决方案有帮助吗?

标签: c# memorystream wkhtmltopdf


【解决方案1】:

这仍然是个问题吗?

在安装 wkhtmltopdf 0.11.0 rc2 后,我刚刚创建了一个新的 ASP.net 网站来在我的计算机上进行测试,它在创建 PDF 时运行良好。我的版本只是略有不同;

在我的 CSHTML 中,我有:

MemoryStream PDFStream = new MemoryStream();
MemoryStream PDF = Derp.GeneratePdf(PDFStream);
byte[] byteArray1 = PDF.ToArray();
PDF.Flush();
PDF.Close();
Response.BufferOutput = true;
Response.Clear();
Response.ClearHeaders();
Response.AddHeader("Content-Disposition", "attachment; filename=Test.pdf");
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(byteArray1);
Response.End();

我的 Derp 课程

public class Derp
{
    public static MemoryStream GeneratePdf(MemoryStream pdf)
    {
        using (StreamReader Html = new StreamReader(@"Z:\HTMLPage.htm"))
        {
            Process p;
            StreamWriter stdin;
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = @"C:\wkhtmltopdf\wkhtmltopdf.exe";
            psi.UseShellExecute = false;
            psi.CreateNoWindow = true;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
            psi.Arguments = "-q -n --disable-smart-shrinking " + " - -";
            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 pdf;
            }
            catch
            {
                return null;
            }
            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);
        }
    }
}

【讨论】:

  • 因此,如果您的输出有问题,您可能需要检查如何将源 HTML 传输到转换器,如果其中有任何问题。
  • 什么是+“--”;在你的论点结束时?
  • @Mvision 我已经将它愚蠢地连接起来,但这意味着我将使用流作为输入和输出,而不是文件作为输入和输出。例如,要让 wkhtmltopdf 从 STDIN 而不是文件或 URL 获取其输入并输出到您将使用 wkhtmltopdf.exe - output.pdf 的文件。类似地使用输入文件并输出到 STDOUT(或者可能是 stderr,不记得了)你会使用wkhtmltopdf.exe input.html -
  • 谢谢!但我会推荐两件事:使用 stream.CopyTo 而不是创建自己的实现。我还将为标准输入使用 utf8 编写器,因为 wkhtmltopdf 默认情况下期望 utf8 并且 Process 默认使用不同的编码:using (StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
  • 优秀点 @Yepeekai - 我已经很多年没有使用 C# 了,而且我现在没有 Windows 机器,所以我对测试/编辑它没有信心 - 随意编辑或者如果您有时间,请添加您自己的答案:)
【解决方案2】:

我对此的看法是基于@Nenotlep 的回答。这只是pdf生成部分。

我正在使用异步。我创建了一个新的 StreamWriter,因为 wkhtmltopdf 默认需要 utf-8,但在进程开始时它被设置为其他值。

我删除了 p.WaitForExit(...),因为如果它失败了我没有处理它,它无论如何都会挂在 await tStandardOutput 上。如果需要超时,则必须使用取消令牌或超时对不同任务调用 Wait 并进行相应处理。

public static async Task<byte[]> GeneratePdf(string html, Size pageSize)
{
    ProcessStartInfo psi = new ProcessStartInfo
    {
        FileName = @"C:\PROGRA~1\WKHTML~1\wkhtmltopdf.exe",
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
        Arguments = "-q -n --disable-smart-shrinking " 
            + (pageSize.IsEmpty ? "" : "--page-width " + pageSize.Width 
            + "mm --page-height " + pageSize.Height + "mm") + " - -";
    };

    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 with errors
        }

        return pdfSream.ToArray();
    }
}

我没有包含在其中但可以作为参考有用的东西:

  • 如果需要,您可以使用 --cookie 传递身份验证 cookie
  • 您可以设置带有 href 的基本标记,指向您的 html 页面的服务器,以便在您的 html 页面中添加其他请求(css、图像等)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-19
    • 2010-11-26
    • 2019-06-20
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-30
    相关资源
    最近更新 更多