slugibihl
slugibihl

Reputation: 47

iTextSharp .Net Action Result to download as a pdf through ajax

I am using iTextSharp to convert a razor view to a pdf that can be downloaded by way of a C# controller. This current implementation works perfectly, however I would like to pass a model from a view to the pdf controller and have the action result download within the browser

Here is my current controller:

public ActionResult Report(MyModel model)
    {
        // The below method is a custom implementation using iTextSharp
        return new FoilPdfActionResult(model, (writer, document) =>
        {
            document.SetPageSize(PageSize.LETTER.Rotate());
            document.NewPage();
        })
        {
            FileDownloadName = "testing.pdf",
        };
    }

Here is my AJAX call:

function sendForm(model) {
        $.ajax({
            type: 'POST',
            url: '@Url.Action("Report")',
                    data: model,
                    success: function (data) {
                        console.log(data);

                    },
                    error: function (xhr, textStatus, errorThrown) {

                    }
                });
    }

If I just visit the path of the controller directly at "/Report" it downloads the file correctly within the browser. If I use the ajax call above to call the controller, it passes the model correctly and returns the pdf result through the data variable on success but does not actually download the pdf. Is is possible to have the pdf downloaded instead of being passed through the data variable?

Upvotes: 1

Views: 2373

Answers (2)

Travis Acton
Travis Acton

Reputation: 4440

Recently has a (2) similar situation and found that there are no real baked in ways to do it via ajax. GOOD NEWS: It's actually really easy to do and you do not need an ajax call.

What you can do is submit a form post request and in your controller post action you can generate the file in memory and use "return File()" to pump the file back to the view without the view reloading.

Here is an example using iTextSharp:

Example Model:

public class TestM
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Number { get; set; }
}

View (basic auto generated create)

@model DeleteMeWeb45.Models.TestM

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>TestM</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.Number, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Number, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Number, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

Controller:

    public ActionResult Index()
    {
        TestM t = new TestM();
        return View(t);
    }
    [HttpPost]
    public ActionResult Index(TestM t)
    {
        if(ModelState.IsValid)
        {
            Document doc = new Document(iTextSharp.text.PageSize.LETTER, 10, 10, 42, 30);
            byte[] pdfBytes;
            BaseFont bfTimes = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
            Font timesBold = new Font(bfTimes, 12, Font.BOLD);

            using (var mem = new MemoryStream())
            {
                PdfWriter wri = PdfWriter.GetInstance(doc, mem);
                doc.SetMargins(20, 20, 20, 60);
                doc.Open();
                var orderInfoTable = new PdfPTable(2);
                orderInfoTable.AddCell(new Phrase("First Name:", timesBold));
                orderInfoTable.AddCell(new Phrase(t.FirstName, timesBold));
                orderInfoTable.AddCell(new Phrase("Last Name:", timesBold));
                orderInfoTable.AddCell(new Phrase(t.LastName, timesBold));
                orderInfoTable.AddCell(new Phrase("Number:", timesBold));
                orderInfoTable.AddCell(new Phrase(t.Number.ToString(), timesBold));

                doc.Add(orderInfoTable);
                doc.Close();
                pdfBytes = mem.ToArray();
            }

            return File(pdfBytes, "application/pdf", "Weeeeeee_" + DateTime.Now.ToString("_MM-dd-yyyy-mm-ss-tt") + ".pdf");
        }
        else
        {
            return View(t);
        }


    }

In the event that you want to keep your form submit button as is (so it saves or does whatever) and want an extra button that just downloads then you would modify the above example as such:

View:

Change 1. Give the form an ID:

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "transferForm" }))

Change 2. Add a button and JS to submit the form at an alternative location:

<div class="row">
    <div class="btn btn-primaryDark btn-sm" id="btnRunReport" style="min-width:120px;">Run Report</div>
</div>



@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

    <script>
        $(document).ready(function () {
            $("#btnRunReport").click(function () {
                $('#transferForm').attr('action', "/Home/JSFUN").submit();
            });

        });
    </script>

}

Controller Changes: 1. Create a new controller action to handle the post request:

    [HttpPost]
    public ActionResult JSFUN(TestM t)
    {
        Document doc = new Document(iTextSharp.text.PageSize.LETTER, 10, 10, 42, 30);
        byte[] pdfBytes;
        BaseFont bfTimes = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, false);
        Font timesBold = new Font(bfTimes, 12, Font.BOLD);

        using (var mem = new MemoryStream())
        {
            PdfWriter wri = PdfWriter.GetInstance(doc, mem);
            doc.SetMargins(20, 20, 20, 60);
            doc.Open();
            var orderInfoTable = new PdfPTable(2);
            orderInfoTable.AddCell(new Phrase("First Name:", timesBold));
            orderInfoTable.AddCell(new Phrase(t.FirstName, timesBold));
            orderInfoTable.AddCell(new Phrase("Last Name:", timesBold));
            orderInfoTable.AddCell(new Phrase(t.LastName, timesBold));
            orderInfoTable.AddCell(new Phrase("Number:", timesBold));
            orderInfoTable.AddCell(new Phrase(t.Number.ToString(), timesBold));

            doc.Add(orderInfoTable);
            doc.Close();
            pdfBytes = mem.ToArray();
        }

        return File(pdfBytes, "application/pdf", "Weeeeeee_" + DateTime.Now.ToString("_MM-dd-yyyy-mm-ss-tt") + ".pdf");

    }

This way your user can use the form submit button and a export button for different purposes.

Upvotes: 1

ManishKumar
ManishKumar

Reputation: 1554

You can create a Iframe element on page and make it hidden and then bind "src" property with complete path of action, it will download your pdf for sure. I have done this many time. You can take help from this link https://www.codeproject.com/Questions/470316/how-to-download-attachment-using-Iframe

Upvotes: 0

Related Questions