【问题标题】:Convert Razor HTML into downloadable PDF将 Razor HTML 转换为可下载的 PDF
【发布时间】:2018-08-30 11:03:42
【问题描述】:

我有一个动态过滤的元素表,每个元素都有一组按钮来操作它们。其中一个显示了使用 Razor 生成的带有元素数据的部分 HTML。

表格条目:

foreach (var item in listado.Where(x => x.Raw != null)) {
<tr>
    <td>@item.Id</td>
    <td>@item.Usuario</td>
    <td>@item.NIF_CIF</td>
    <td>
        @if (!(String.IsNullOrEmpty(item.Telefono)) && !(String.IsNullOrWhiteSpace(item.Telefono))) {
            @item.Telefono
        } else {
            @Html.Raw("No disponible")}
    </td>
    <td>@Math.Round(item.Raw.PrecioTotal).ToString("0.##")&nbsp;&euro;</td>
    <td>@item.FechaCreacion.ToShortDateString()</td>
    <td class="btn-group-sm" role="group">
        <button class="eliminar btn btn-secondary btn-danger" data-itemid="@item.Id">Eliminar</button>
        <button class="convertirPDF btn btn-secondary btn-info" data-itemid="@item.Id">PDF</button>
        <button class="detalles btn brn-secondary btn-info" data-itemid="@item.Id">Detalles</button></td>
</tr>
<tr id="vistaDetalles@(item.Id)" hidden>
    <td colspan="7">
        @Html.Partial("_PresupuestoFinal", item)
    </td>
</tr> }

我要使用的元素在该行中生成

@Html.Partial("_PresupuestoFinal", item)

然后,我使用 jQuery 为这些按钮提供功能。 PDF按钮代码为:

$(".convertirPDF").on("click", function (id) {
    var itemId = $(this).data('itemid');
    var presupuesto = $('#content').html($(this).find('#vistaDetalles' + itemId).html());
    Pdf(presupuesto);
});

function Pdf(presupuesto) {
    presupuestoHTML = presupuesto;

    $.ajax({
        method: "GET",
        url: 'DescargarPDF',
        data: { presupuestoHTML: presupuesto },
        cache: false,
        async: true,
    });
};

但每次我按下按钮,我都会进入控制台:

TypeError: can't convert undefined to object

当我调用函数 Pdf 时引用该行。我想要做的是选择特定的 html(与元素对应的那个)并将其转换为 PDF 文件。由于我无法获得足够的信息来知道错误在哪里,我做错了什么?

顺便说一句,url: 'DescargarPDF 行指向我的控制器中的方法。

编辑

这是我要求的控制器方法:

public void DescargarPDF (string presupuestoHTML) {

        Response.Clear();
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-disposition", "attachment;filename=" + "PDF.pdf");
        Response.Cache.SetCacheability(HttpCacheability.NoCache);

        MemoryStream ms = new MemoryStream();
        TextReader txtReader = new StringReader(presupuestoHTML);

        // Creamos un objeto de clase itextsharp document
        Document doc = new Document(PageSize.A4, 25, 25, 30, 30);

        // Creamos un pdfwriter de itextsharp que lee el documento y dirige un stream XML a un archivo
        PdfWriter oPdfWriter = PdfWriter.GetInstance(doc, ms);

        // Creamos un trabajador para parsear el documento
        HTMLWorker htmlWorker = new HTMLWorker(doc);

        // Abrimos el documento y se lo pasamos al trabajador
        doc.Open();
        htmlWorker.StartDocument();

        // Parseamos el presupuesto en html al documento
        htmlWorker.Parse(txtReader);

        // Cerramos el documento y el trabajador
        htmlWorker.EndDocument();
        htmlWorker.Close();
        doc.Close();

        var bPDF = ms.ToArray();

        Response.BinaryWrite(bPDF);
        Response.End();
    }

脚本没有命中。

第二次编辑

所以我得到了有关错误的更多信息:

presupuestoHTML[toArray]

是什么导致了错误。不知道在哪里,也不知道如何。

第三次编辑

好的,我现在知道问题出在检索我想要的 html 的脚本上。我改变了那行:

$(".convertirPDF").on("click", function (id) {
    itemId = $(this).data('itemid');
    presupuesto = document.documentElement.getElementsByTagName('vistaDetalles' + itemId);
    Pdf(presupuesto);
});

现在控制台中的错误是:

TypeError: 'item' called on an object that does not implement interface HTMLCollection.

我会继续研究它,希望这能让问题更清楚。

第二次更新

我一直在努力,除了文件下载之外,我一切正常。我来解释一下:

这是脚本,它使用正确的数据访问控制器方法。

$(".convertirPDF").on("click", function (id) {
    var itemId = $(this).data('itemid');
    // var presupuesto = $('#vistaDetalles' + itemId).html();
    Pdf(itemId);
});

function Pdf(itemid) {
    var id = itemid;

    $.ajax({
        method: "POST",
        url: 'DescargarPDF',
        data: { itemId: id },
        cache: false,
        async: true,
    });
};

这样就行了。这是我的控制器方法,我怀疑问题出在哪里:

 public FileResult DescargarPDF (int itemId) {
        var presupuesto = ReglasNegocio.Fachada.Consultas.ObtenerPresupuesto(itemId);             
        var archivo = new Rotativa.PartialViewAsPdf("_PresupuestoFinal", presupuesto) { FileName = "Presupuesto_" + itemId + ".pdf" };

        return File(archivo.FileName, "application/pdf");
    }

这是我迄今为止的最后一次尝试。我也试过Rotativa的ActionAsPdf,我在浏览器控制台上得到了一个数据流(我猜它是pdf文件)而不是可下载的文件。我还尝试了其他选项,例如将文件转换为字节数组并将其流式传输,但由于我不想保存文件,因此该选项被丢弃(函数需要文件路径,我无法提供,因为该文件未保存,仍在内存中)。仍在努力。

【问题讨论】:

  • 这一切在我看来都是合理的。您可以在匿名单击处理程序函数中设置断点并单步执行吗?也可能值得console.log() -ging 一切,看看您是否可以发现未定义的内容,例如Pdf 函数,presupuesto,并将控制台日志也放入 Pdf 函数中,以查看您是否真的将其放入函数中。
  • 请求是否到达控制器?你能显示方法的代码吗?
  • 感谢您的回答。不,我认为请求没有命中控制器,错误消息来自浏览器(firefox)调试控制台。
  • 你熟悉调试吗?我建议调试客户端(firefox 上的 js 文件)和后端(控制器)以检查究竟是什么问题。 debug js linkdebug C# link
  • @matramos 是的,这就是我在问题中提到的错误的原因。但是,我无法通过集成在浏览器上的开发人员工具获得更多信息(或者我可能没有正确使用它们),但我会继续寻找。谢谢。

标签: c# jquery .net ajax asp.net-mvc


【解决方案1】:

我请了另一个question尝试解决无法下载文件的问题,我明白了。

在任何事情之前,我都是通过 ajax 发出请求,因为我认为这是一个很好的方法,但事实证明,有一个更简单的方法:不使用 ajax。

所以,我删除了按钮的脚本和按钮本身,所以现在看起来像这样:

<a href="DescargarPDF/?itemId=@item.Id" target="_blank" class="btn btn-secondary btn-info">PDF</a>

它的外观与以前相同,但实际上是指向我的控制器方法的链接,现在看起来像这样:

public FileResult DescargarPDF (int itemId) {
        var presupuesto = ReglasNegocio.Fachada.Consultas.ObtenerPresupuesto(itemId);             
        var archivo = new Rotativa.PartialViewAsPdf("_PresupuestoFinal", presupuesto) { FileName = "Presupuesto_" + itemId + ".pdf", PageSize = Rotativa.Options.Size.A4 };
        var binario = archivo.BuildFile(this.ControllerContext);        

        return File(binario, "application/pdf", archivo.FileName);           
    }

我知道在大多数情况下这不是一个有效的解决方案,因为我只是将 ajax 抛在脑后,但还有许多其他问题的答案对他们有用,他们仍然使用 ajax 来管理请求。

不过,我希望这会有所帮助。谢谢大家。编码愉快。

更新

我刚刚发现为什么我的 PDF 文件被放入控制台,请查看 the other question,我对这个特定问题做了一些解释。谢谢大家。

【讨论】:

    【解决方案2】:

    您只能使用 javascript:

    【讨论】:

    • 非常感谢您的回答,我会将其保留在我的实用程序下,但遗憾的是这是我正在开发的一个学习 C# 的项目,所以我必须使用它。但再次感谢您。
    猜你喜欢
    • 2013-08-21
    • 2012-04-04
    • 2018-02-11
    • 2011-09-02
    • 2010-12-31
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 2016-12-01
    相关资源
    最近更新 更多