How does HttpContext.Current.Response.Binarywrite

2019-09-11 09:28发布

问题:

Let me start by saying if I'm making this overly complicated, please, please offer suggestions on how to simplify!! I'm trying to create some logic that will let users export the contents on the screen to a PDF file. The contents contains multiple highcharts graphs, so I need for the html to be rendered completely to the page so I can grab the svg information to pass into the PDF file. Since the page has been rendered completely, the only way I know to pass the rendered html back to the server is through Web Services. Web Services is calling a .cs file where the PDF functionality is located. I can set a break point in the .cs file at the end of the pdf function, and it is hitting it. The problem is, it's returning an error of "200 OK parsererror SyntaxError: Unexpected token %".

Now, I create a dummy html string in my C# codebehind and call the .cs file from the codebehind, and it creates a PDF file with no issues at all. So my question is, why would the class work when being called from a codebehind, but not from Web Services? The only thing I can think of, is that it has something to do with a postback? My code is below:

JavaScript that is calling the Web Service..I can step all the way through the web service and class functions being called, they are making it to the end, but it's returning to the error function with 200 OK parsererror SyntaxError: Unexpected token %

function checkSplash() {
    timesChecked++;
    if (loadImages >= document.getElementsByName('img').length || timesChecked * checkInterval >= maxLoadTime) {
        clearInterval(intervalID);

        if (document.getElementById("exportDiv") != null) {
            var mydata = {
                src: document.getElementById("export_pdf").innerHTML
            }

            $.ajax({
                type: "post",
                contentType: "application/json; charset=UTF-8",
                url: "../WebServices/print.asmx/export",
                data: JSON.stringify(mydata),
                dataType: "json",
                success: function (response) {
                    alert("here");
                    location.reload(true);
                },
                error: function (xhr, textStatus, errorThrown) {
                    alert(xhr.status + ' ' + xhr.statusText + ' ' + textStatus + ' ' + errorThrown);
                }
            })
        }
    }
}

Web Service being called...this function is identical the a test function I created in the codebehind. The codebehind test function works and creates the PDF document with no issues. But when this gets called from the javascript, it returns the error

using DMC.Classes;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Web;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace DMC.WebServices
{
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    [System.Web.Script.Services.ScriptService]

    public class print : System.Web.Services.WebService
    {
        [WebMethod]
        public bool export(string src)
        {
            string fileName = null;
            export dmc = new export();

            fileName = " Behavior Statistics YTD ";

            dmc.exportPDF(fileName, "Portrait", src);

            return true;
        }
    }
}

Finally, the .cs methods being used:

using iTextSharp.text;
using iTextSharp.text.html.simpleparser;
using iTextSharp.text.pdf;
using NReco.PdfGenerator;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace DMC.Classes
{
    public class export 
    {
        public void exportPDF(string fileName, string Orientation, string html)
        {
            HtmlToPdfConverter pdf = new HtmlToPdfConverter();

            html = html.Replace("\n", "");
            html = html.Replace("\t", "");
            html = html.Replace("\r", "");
            html = html.Replace("\"", "'");

            switch (Orientation)
            {
                case "Portrait":
                    pdf.Orientation = PageOrientation.Portrait;
                    break;
                case "Landscape":
                    pdf.Orientation = PageOrientation.Landscape;
                    break;
                default:
                    pdf.Orientation = PageOrientation.Default;
                    break;
            }

            pdf.Margins.Top = 25;
            pdf.PageFooterHtml = createPDFFooter();

            var pdfBytes = pdf.GeneratePdf(createPDFScript() + html + "</body></html>");

            HttpContext.Current.Response.ContentType = "application/pdf";
            HttpContext.Current.Response.ContentEncoding =  System.Text.Encoding.UTF8;
            HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + fileName + ".pdf");
            HttpContext.Current.Response.BinaryWrite(pdfBytes);
            HttpContext.Current.Response.Flush();
            HttpContext.Current.Response.End();
        }

        private string createPDFScript()
        {
            return "<html><head><style>td,th{line-height:20px;} tr { page-break-inside: avoid }</style><script>function subst() {var vars={};var x=document.location.search.substring(1).split('&');for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}" +
        "var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];for(var i in x) {var y = document.getElementsByClassName(x[i]);" +
        "for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];}}</script></head><body onload=\"subst()\">";
        }

        private string createPDFFooter()
        {
            return "<div><table style='font-family:Tahoma; font-size:9px; width:100%'><tr><td style='text-align:left'>Research Dept|RR:mm:jpg</td><td style='text-align:right'>Page <span class=\"page\"></span> of <span class=\"topage\"></span></td></div>";
        }
    }
}

回答1:

In your exportPDF function, add a try-catch to trap the Response.End() exception and ignore it:

public void exportPDF(string fileName, string Orientation, string html)
{
    try
    {
        HtmlToPdfConverter pdf = new HtmlToPdfConverter();

        html = html.Replace("\n", "");
        html = html.Replace("\t", "");
        html = html.Replace("\r", "");
        html = html.Replace("\"", "'");

        switch (Orientation)
        {
            case "Portrait":
                pdf.Orientation = PageOrientation.Portrait;
                break;
            case "Landscape":
                pdf.Orientation = PageOrientation.Landscape;
                break;
            default:
                pdf.Orientation = PageOrientation.Default;
                break;
        }

        pdf.Margins.Top = 25;
        pdf.PageFooterHtml = createPDFFooter();

        var pdfBytes = pdf.GeneratePdf(createPDFScript() + html + "</body></html>");
        HttpContext.Current.Response.ContentType = "application/pdf";
        HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.UTF8;
        HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + fileName + ".pdf");
        HttpContext.Current.Response.BinaryWrite(pdfBytes);
        HttpContext.Current.Response.Flush();
        HttpContext.Current.Response.End();
    }
    catch
    {
        //if you have other exceptions you'd like to trap for, you can filter or throw them here
        //otherwise, just ignore your Response.End() exception.
    }
}


回答2:

Referencing HttpContext from within the PDF creator class really is not a good idea as it violates the principal of Separation of Concerns. A better way would be for your exportPDF method to return the pdfBytes array and have the caller can handle it appropriately.

So your exportPDF method ends up looking like this:

public byte[] exportPDF(string fileName, string Orientation, string html)
{
    // etc.

    var pdfBytes = pdf.GeneratePdf(createPDFScript() + html + "</body></html>");

    return pdfBytes;
}

And your web method looks like this:

[WebMethod]
public byte[] export(string src)
{
    string fileName = null;
    export dmc = new export();

    fileName = " Behavior Statistics YTD ";

    Context.Response.ContentType = "application/pdf";
    Context.Response.ContentEncoding =  System.Text.Encoding.UTF8;
    Context.Response.AddHeader("content-disposition", "attachment; filename=" + fileName + ".pdf");

    return dmc.exportPDF(fileName, "Portrait", src);
}

One thing that isn't clear to me is how you are planning to handle the response in the JavaScript. What this returns is the PDF file content. What is your JavaScript going to do with that?