Reputation: 47
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
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
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