如何将生成的 HTML 存储为字符串,而不是使用 Razor 视图引擎写入浏览器?

发布于 2024-10-19 18:43:32 字数 3217 浏览 0 评论 0原文

我使用 MVC3 和 Razor 视图引擎创建了一个网站。我想要做的是将生成的 HTML 存储在流或字符串中,以便我可以将其写入文件而不是写入浏览器。

我需要做的是将生成的 HTML 转换为 PDF,并将 PDF 作为报告形式提供给用户。我已经解决了这部分问题,只是无法找出将 HTML 放入某种变量的最佳方法。

编辑 - 我最终走向了一点不同的方向,并想分享结果。我创建了一个使用 WKHTMLTOPDF 项目将流转换为 PDF 的属性。现在我所做的就是向操作添加一个属性,而不是将 HTML 渲染到浏览器,而是弹出一个“另存为”对话框。

public class PdfInterceptAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResult;
        var workingDir = ConfigurationManager.AppSettings["PdfWorkingPath"];
        var fileName = workingDir + @"\" + Guid.NewGuid() + ".pdf";

        if (viewResult != null)
        {
            var view = viewResult.View;
            var writer = new StringWriter();
            var viewContext = new ViewContext(filterContext.Controller.ControllerContext, view,
                viewResult.ViewData, viewResult.TempData, writer);
            view.Render(viewContext, writer);
            HtmlToPdf(new StringBuilder(writer.ToString()), fileName);
            filterContext.HttpContext.Response.Clear();
            var pdfByte = File.ReadAllBytes(fileName);
            filterContext.HttpContext.Response.ContentType = "application/pdf";
            filterContext.HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename=Report.pdf");
            filterContext.HttpContext.Response.BinaryWrite(pdfByte);
            filterContext.HttpContext.Response.End();
        }

        base.OnResultExecuted(filterContext);
    }

    private static bool HtmlToPdf(StringBuilder file, string fileName)
    {
        // assemble destination PDF file name

        var workingDir = ConfigurationManager.AppSettings["PdfWorkingPath"];
        var exePath = ConfigurationManager.AppSettings["PdfExePath"]; //Path to the WKHTMLTOPDF executable.
        var p = new Process
                    {
                        StartInfo = {FileName = @"""" + exePath + @""""}
                    };

        var switches = "--print-media-type ";
        switches += "--margin-top 4mm --margin-bottom 4mm --margin-right 0mm --margin-left 0mm ";
        switches += "--page-size A4 ";

        p.StartInfo.Arguments = switches + " " + "-" + " " + 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 = workingDir;

        p.Start();
        var sw = p.StandardInput;
        sw.Write(file.ToString());
        sw.Close();

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

I have created a website using MVC3 along with the Razor view engine. What I want to do is take the resulting HTML and store it in a stream or string so that I can write it to a file instead of writing it to the browser.

What I need to do is take the resulting HTML and convert it to PDF and supply the PDF to the user as a form of reporting. I have that portion of it worked out already, I just can't figure out the best way to get the HTML in to a variable of some sort.

EDIT - I ended up going in a little bit different direction and wanted to share the results. I created an attribute that uses the WKHTMLTOPDF project to convert the stream to PDF. Now all I do is add an attribute to the action and instead of rendering the HTML to the browser it pops up a save as dialog.

public class PdfInterceptAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResult;
        var workingDir = ConfigurationManager.AppSettings["PdfWorkingPath"];
        var fileName = workingDir + @"\" + Guid.NewGuid() + ".pdf";

        if (viewResult != null)
        {
            var view = viewResult.View;
            var writer = new StringWriter();
            var viewContext = new ViewContext(filterContext.Controller.ControllerContext, view,
                viewResult.ViewData, viewResult.TempData, writer);
            view.Render(viewContext, writer);
            HtmlToPdf(new StringBuilder(writer.ToString()), fileName);
            filterContext.HttpContext.Response.Clear();
            var pdfByte = File.ReadAllBytes(fileName);
            filterContext.HttpContext.Response.ContentType = "application/pdf";
            filterContext.HttpContext.Response.AddHeader("Content-Disposition", "attachment; filename=Report.pdf");
            filterContext.HttpContext.Response.BinaryWrite(pdfByte);
            filterContext.HttpContext.Response.End();
        }

        base.OnResultExecuted(filterContext);
    }

    private static bool HtmlToPdf(StringBuilder file, string fileName)
    {
        // assemble destination PDF file name

        var workingDir = ConfigurationManager.AppSettings["PdfWorkingPath"];
        var exePath = ConfigurationManager.AppSettings["PdfExePath"]; //Path to the WKHTMLTOPDF executable.
        var p = new Process
                    {
                        StartInfo = {FileName = @"""" + exePath + @""""}
                    };

        var switches = "--print-media-type ";
        switches += "--margin-top 4mm --margin-bottom 4mm --margin-right 0mm --margin-left 0mm ";
        switches += "--page-size A4 ";

        p.StartInfo.Arguments = switches + " " + "-" + " " + 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 = workingDir;

        p.Start();
        var sw = p.StandardInput;
        sw.Write(file.ToString());
        sw.Close();

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜`诱少女 2024-10-26 18:43:32

我使用这段代码:

private string RenderView<TModel>(string viewPath, TModel model, TempDataDictionary tempData = null) {
    var view = new RazorView(
        ControllerContext,
        viewPath: viewPath,
        layoutPath: null,
        runViewStartPages: false,
        viewStartFileExtensions: null
    );

    var writer = new StringWriter();
    var viewContext = new ViewContext(ControllerContext, view, new ViewDataDictionary<TModel>(model), tempData ?? new TempDataDictionary(), writer);
    view.Render(viewContext, writer);
    return writer.ToString();
}

这使用当前的ControllerContext;如果您不希望这样,则需要模拟 HttpContextBase

如果您想从视图传回数据,则需要将其传递到 TempData,而不是 ViewBag

I use this code:

private string RenderView<TModel>(string viewPath, TModel model, TempDataDictionary tempData = null) {
    var view = new RazorView(
        ControllerContext,
        viewPath: viewPath,
        layoutPath: null,
        runViewStartPages: false,
        viewStartFileExtensions: null
    );

    var writer = new StringWriter();
    var viewContext = new ViewContext(ControllerContext, view, new ViewDataDictionary<TModel>(model), tempData ?? new TempDataDictionary(), writer);
    view.Render(viewContext, writer);
    return writer.ToString();
}

This uses the current ControllerContext; if you don't want that, you'll need to mock an HttpContextBase.

If you want to pass data back from the view, you'll need to pass it in TempData, not ViewBag.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文