Export to Excel Not Working in WEB API - c#

I am trying to export to excel a JQGrid data from WEB API controller ..Need to handle it from a controller method ..Everything is going fine but Excel is not showing ....
<input type="image" src="../images/Excel.jpg" alt="Submit" width="20" height="20"
id="btnExport" runat="server" onclick="ExportData();" />
function ExportData() {
//debugger;
var tblHTML = createGridStructureForPrint($("#table3"));
tblHTML = escape(tblHTML);
var ExportData = tblHTML;
var source = {
'ExportData': ExportData
};
var apiUrlExport = '../api/Home/ExportData?source=' + source ;
$.ajax({
type: "POST",
dataType: "json",
url: apiUrlExport,
beforeSend: function () {
$('#dvStatus').show("slow");
},
data: JSON.stringify(source),
contentType: 'application/json; charset=utf-8',
success: function (data) {
debugger;
},
error: function (ex) {
},
complete: function () {
$('#dvStatus').hide("slow");
}
});
};
[HttpPost]
public void ExportData(ExportToExcel source)
{
try
{
string data = source.ExportData;
data = HttpUtility.UrlDecode(data);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=Data.xls");
HttpContext.Current.Response.ContentType = "application/excel";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.Unicode;
HttpContext.Current.Response.BinaryWrite(System.Text.Encoding.Unicode.GetPreamble());
System.IO.StringWriter sw = new System.IO.StringWriter();
sw.Write(data.ToString());
System.Web.UI.HtmlTextWriter hw = new HtmlTextWriter(sw);
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
}
catch (Exception ex)
{
throw ex;
}
}
Not sure where i am doing wrong ..

Rahul, you might want to look into JQGrid.ExportSettings and JQGrid.ExportToExcel. I have found it simpler than Http Response...

Related

Link inside Link web method

Hi i am trying to navigate through anchor link. Only the first link works. For example if i click a link in a page it goes to that link, but when there is another link in page whcih i have clicked it doesnt work. And also the second has the same class 1st link Please help.
$('#frmDisplay').on('load', function () {
$('#frmDisplay a.anchorLink').on('click', function () {
var id = $(this).attr('id');
var hid = document.getElementById('<%= HiddenField1.ClientID %>');
hid.value = id;
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Amm.aspx/getlink",
data: "{'Id': '" + id + "'}",
dataType: "json",
success: function (data) {
$('#frmDisplay').contents().find('html').html(data.d);
},
error: function (response) {
alert(response.responseText);
}
});
});
});
public static string getlink(int Id)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["connString"].ConnectionString);
string link = "extlink";
BookTree obj = new BookTree();
DataSet ds = obj.getlink(Id);
SqlCommand cmd=new SqlCommand("select vcFilePath from tblBookNodes where iModuleId='" + Id + "'",conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
bytes = (byte[])dr["vcFilePath"];
}
string fileName = link.Replace(" ", "_") + ".htm";
// DirectoryInfo strPath = new DirectoryInfo(HttpContext.Current.Server.MapPath(#"~/Linking/"));
//string strPath = HttpContext.Current.Server.MapPath(#"/Linking/") + fileName;
//foreach (FileInfo file in strPath.GetFiles())
//{
// file.Delete();
//}
string path = Path.Combine(HttpContext.Current.Server.MapPath("~/htmlFile/"), fileName);
var doc = new HtmlDocument();
string html = Encoding.UTF8.GetString(bytes);
doc.LoadHtml(html);
StringWriter sw = new StringWriter();
var hw = new HtmlTextWriter(sw);
StreamWriter sWriter = new StreamWriter(path);
sWriter.Write(sw.ToString());
doc.Save(sWriter);
sWriter.Close();
//string fileContents = html;
//System.IO.File.WriteAllText(path, html);
return File.ReadAllText(path);
}
You have to use "on" instead of find() and event listener attach.
Please refer to this example:
$('#formDisplay a.anchroLink').on('click', function() {
// TODO: handle the click
})
This is necessary because when you add the event listener the DOM is not yet ready to handle requests for non existing content. Using "on" you can attach an event to a class or a simple DOM query.
To be more clear I post your code with modifications based on my previous suggestion:
$('#frmDisplay a.anchorLink').on('click', function () {
var id = $(this).attr('id');
var hid = document.getElementById('<%= HiddenField1.ClientID %>');
hid.value = id;
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Amm.aspx/getlink",
data: "{'Id': '" + id + "'}",
dataType: "json",
success: function (data) {
$('#frmDisplay').contents().find('html').html(data.d)
},
error: function (response) {
alert(response.responseText);
}
});
});
My last answer (I don't know you were using iFrames):
function clickHandler() {
var id = $(this).attr('id');
var hid = document.getElementById('<%= HiddenField1.ClientID %>');
hid.value = id;
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Amm.aspx/getlink",
data: "{'Id': '" + id + "'}",
dataType: "json",
success: function (data) {
$('#frmDisplay').contents().find('html').html(data.d);
// You can reload the event handler because the prev one
// has been lost after refreshing the page with the ajax call.
$('#frmDisplay').contents()
.find('a.anchorLink')
.click(clickHandler);
},
error: function (response) {
alert(response.responseText);
}
});
}
$('#frmDisplay').on('load', function () {
$('#frmDisplay')
.contents()
.find('a.anchorLink')
.on('click', clickHandler);
})

Ajax call to async method returning file successfully but success/complete part of an Ajax request is not getting executed

I am trying to export selected records in to a file and reload the page to update the records in a current view. I am calling web api asynchronously to get all the records. An AJAX call is executing an action in a controller successfully and returning expected data without any error but none of the 'success', 'complete' or 'error' part of ajax function is executing. There are no errors in a developer tool of the browser, no exception, nothing unusual so its getting trickier for me to investigate this issue further. Can I request your a suggestions on this please? Thanks
View :
#Html.ActionLink("Export records", "Index", null, new { Id = "myExportLinkId")
Script :
$("a#myExportLinkId").click(function (e) {
var selected = "";
$('input#myCheckBoxList').each(function () {
if (this.checked == true) {
selected += $(this).val() + ',';
}
});
if (selected != "") {
$.ajax({
url: '/MyController/MyAction',
type: 'GET',
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
'MyString': 'stringValue'
},
success: function (data) {
alert("success");
},
error: function () {
alert("error");
}
});
})
And the action/method looks like this :
[HttpGet]
public async Task<ActionResult> ExportNewOrders(string OrderIdString)
{
//code to create and store file
//actually want to send the file details as json/jsonResult but for testing only returning
//string here
return Json( "Success", "application/json", JsonRequestBehavior.AllowGet);
}
Finally I have resolved this with Promisify functionality of an AJAX call. Obviously the json response I was returning had an issue so I have replaced
return Json( "Success", "application/json", JsonRequestBehavior.AllowGet);
to
return new JsonResult(){
Data = new { success = true, guid = handle, fileName = exportFileName },
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
which has fixed the bug and the success function of ajax call got executed.
But other than this there were issues to wait until the file download (which involved encryption decryption, server validations etc) completes and then refresh the page. This I have resolved by implementing an ajax call with Promisify fuctionality. You can find codepen example here and the original post here.
Here is the complete code.
View/HTML
#Html.ActionLink("Export", "yourActionName", null, new { Id = "exportRequest", #onclick = "letMeKnowMyFileIsDownloaded();" })
Script/Ajax
function letMeKnowMyFileIsDownloaded() {
return new Promise(function (resolve, reject) {
$("a#exportRequest").on("click", function () {
$.ajax({
url: this.href + "?param=whatever params you want to pass",
dataType: "json",
data: {
'param1': 'value'
},
success: function (data) {
var a = document.createElement("a");
var url = '/yourControllerName/Download?fileGuid=' + data.guid + '&filename=' + data.fileName;//window.URL.createObjectURL(data);
a.href = url;
a.download = data.fileName;
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
resolve(true);
},
error: function (error) {
reject(error);
}
});
});
});
}
letMeKnowMyFileIsDownloaded()
.then(function (bool) {
if (bool) {
//alert("File downloaded 👇");
window.location.reload(1);
}
})
.catch(function (error) {
alert("error");
});
I have used nuget package ClosedXML to handle excel file functionality. Using the stream to create and download the data in excel file without storing the file physically on the server.
And in the controller
//can be async or sync action
public async Task<ActionResult> Index(YourModel model)
{
//do stuff you want
var exportOrders = your_object;
//using DataTable as datasource
var dataSource = new DataTable();
//write your own function to convert your_object to your_dataSource_type
dataSource = FormatTypeToDataTable(exportOrders);
if (dataSource != null && dataSource.Rows.Count > 0)
{
//install ClosedXML.Excel from nuget
using (XLWorkbook wb = new XLWorkbook())
{
try
{
var handle = Guid.NewGuid().ToString();
wb.Worksheets.Add(dataSource, "anyNameForSheet");
string exportFileName = "yourFileName" + ".xlsx";
MemoryStream stream = GetStream(wb);
TempData[handle] = stream; exportFileName);
return new JsonResult()
{
Data = new { success = true, guid = handle, fileName = exportFileName },
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
catch (Exception ex)
{
//ModelState.AddModelError("", ex.Message);
}
}
}
}
public virtual ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
var stream = TempData[fileGuid] as MemoryStream;
var data = stream.ToArray();
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
return new EmptyResult();
}
}

Send a binary excel file data from memory stream and generic handler to client side and prompt save

Currently I am using JQuery AJAX and connect it to the Generic Handler (.ashx), inside the Generic Handler, I am doing the template for the Excel File that is going to save the computer (prompt save). i can achieve that while using the .aspx.cs , the reason I am doing from Generic Handler (.ashx), is because I don't know any way to hide the loading overlay after the dialog box for us to save to the computer is appear. (means the loading overlay just stick there). $(element).show() or $(element).hide() is to show or hide the loading overlay.
I am using EPPlus Library to generate the template for Excel File, however I don't know how to pass the object into client side and then prompt to save, it keeps gives me an error.
The problem I was encountered while using the Generic Handler is always gives me the parsererror message.
Here is the code that I am using:
JQuery AJAX:
var loading = $("#loading");
$("#template").click(function () {
loading.show();
$.ajax({
type: "POST",
url: "TemplateService.ashx?Month=" + $("#Month").val() + "&Year=" + $("#Year").val(),
data: {},
contentType: "application/json",
dataType: "json",
success: function (data) {
loading.hide();
console.log(data);
alert("Success");
},
error: function (xhr, text) {
loading.hide();
console.log(JSON.stringify(xhr.responseText));
console.log(JSON.stringify(text));
alert("There is a problem while processing your request");
}
});
});
Generic Handler (.ashx):
public class TemplateService : IHttpHandler, IRequiresSessionState
{
private int Month = 0;
private int Year = 0;
public void ProcessRequest(HttpContext context)
{
Month = Convert.ToInt32(context.Request.Params["Month"]);
Year = Convert.ToInt32(context.Request.Params["Year"]);
try
{
string MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(Month);
string fileName = string.Format("{0} - {1} {2}.xlsx", "Excel Template", MonthName, Year);
using (var package = new ExcelPackage())
{
package.Export();
using (var stream = new MemoryStream())
{
context.Response.Clear();
context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
context.Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", fileName));
package.SaveAs(stream);
stream.WriteTo(context.Response.OutputStream);
context.Response.Write(stream);
}
}
}
catch (Exception)
{
ProcessResponse(context, Response());
}
}
private string Response(bool isSuccess = false)
{
string status = (isSuccess) ? Constant.SUCCESS : Constant.FAILED;
return JsonConvert.SerializeObject(new
{
Status = status
});
}
private void ProcessResponse(HttpContext context, string response)
{
context.Response.Clear();
context.Response.ContentType = "application/json";
context.Response.Write(response);
}
public bool IsReusable
{
get
{
return false;
}
}
}
Export function:
public void Export(this ExcelPackage package)
{
try
{
package.Workbook.Worksheets.Add("Excel Template");
var workSheet = package.Workbook.Worksheets["Excel Template"];
var columnNames = new string[]
{
"First Column",
"Second Column",
"Third Column"
};
var headerRow = new List<string[]>()
{
columnNames
};
var cells = workSheet.Cells[1, 1, 1, 3];
cells.LoadFromArrays(headerRow);
cells.AutoFilter = true;
cells.AutoFitColumns();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
HTML:
<form>
<asp:Button ID="Submission" runat="server" OnClick="Submission_Click" Text="Upload" />
<input type="button" id="Template" value="Template" />
</form>
<script type="text/javascript">
var loading = $("#loading");
$("form").submit(function () {
loading.show();
});
$("#template").click(function () {
loading.show();
$.ajax({
type: "POST",
url: "TemplateService.ashx?Month=" + $("#Month").val() + "&Year=" + $("#Year").val(),
data: {},
contentType: "application/json",
dataType: "json",
success: function (data) {
loading.hide();
console.log(data);
alert("Success");
},
error: function (xhr, text) {
loading.hide();
console.log(JSON.stringify(xhr.responseText));
console.log(JSON.stringify(text));
alert("There is a problem while processing your request");
}
});
});
</script>
Your answer much appreciated.
Thank you.

Displaying PDF from byte[] in MVC 4

I'm using Grid.MVC to display data from an entity model. On row click I am getting the value of a cell and passing it to my controller with a json/ajax function.
In my controller the int "ticketnumber" is passing just fine. The thing that I am not understanding is when I hard code the int, it is working (if I directly browse to http://localhost:58779/ticket/PDFVIEW).
The controller seems to be running through just fine, but it is not displaying the PDF..it just takes me back to my grid in my view with the ajax script. Thanks for the help.
Edit - Code:
<script>
$(function () {
pageGrids.TicketGrid.onRowSelect(function (e) {
var ticketnumber = e.row.UnsettledID;
ticketnumber = JSON.stringify({ 'ticketnumber': ticketnumber });
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: '/ticket/PDFVIEW',
data: ticketnumber,
});
});
});
</script>
controller:
[ActionName("PDFVIEW")]
[HttpGet]
public ActionResult PDFVIEW(int ticketnumber)
{
var db = new ScaleTrac_VerticalEntities();
Ticket_UnsettledScaleImages tu = new Ticket_UnsettledScaleImages();
tu = db.Ticket_UnsettledScaleImages.Where(p => p.UnsettledID == ticketnumber).First();
string filename = "ScaleTick" + tu.UnsettledID + ".pdf";
{
byte[] bytes = tu.ScaleTicket;
TempData["bytes"] = bytes;
Response.Clear();
MemoryStream ms = new MemoryStream(bytes);
return new FileStreamResult(ms, "application/pdf");
}
}
You can't use AJAX to download a file in this way. Your AJAX code is getting the contents of the PDF, but your browser needs to receive it as a normal request in order to view it. You should instead render a link to the PdfView action, or use window.setLocation if you need to do it from a Javascript event handler.
Note you'll also need to change your action method to accept HttpGet.
Using what Richard said helped a lot.
My Json I changed to:
<script>
$(function pdfviewer() {
pageGrids.TicketGrid.onRowSelect(function (e) {
var ticketnumber = e.row.UnsettledID;
ticketnumber = JSON.stringify({ 'ticketnumber': ticketnumber });
$.ajax({
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
url: '/ticket/PDFVIEW',
data: ticketnumber,
success: function (d) {
if (d.success) {
window.location = "/Ticket/DownloadFile" + "?fName=" + d.fName;
}
},
error: function () {
alert("Error");
}
});
});
});
</script>
And in my controller I did:
[ActionName("PDFVIEW")]
public ActionResult pdf(int ticketnumber)
{
var db = new ScaleTrac_VerticalEntities();
Ticket_UnsettledScaleImages tu = new Ticket_UnsettledScaleImages();
tu = db.Ticket_UnsettledScaleImages.Where(p => p.UnsettledID == ticketnumber).First();
string filename = "ScaleTick" + tu.UnsettledID + ".pdf";
{
byte[] bytes = tu.ScaleTicket;
TempData["bytes"] = bytes;
Response.Clear();
MemoryStream ms = new MemoryStream(bytes);
var fName = string.Format("File-{0}.pdf", DateTime.Now.ToString("s"));
Session[fName] = ms;
return Json(new { success = true, fName }, JsonRequestBehavior.AllowGet);
}
}
public ActionResult DownloadFile(string fName)
{
var ms = Session[fName] as MemoryStream;
if (ms == null)
return new EmptyResult();
Session[fName] = null;
return File(ms, "application/pdf", fName);
}
Thank you very much!

How to download file via jQuery ajax and C#

I want to download a file using jQuery Ajax web method, but it's not working.
Here is my jQuery ajax call to web method:
function GenerateExcel() {
var ResultTable = jQuery('<div/>').append(jQuery('<table/>').append($('.hDivBox').find('thead').clone()).append($('.bDiv').find('tbody').clone()));
var list = [$(ResultTable).html()];
var jsonText = JSON.stringify({ list: list });
$.ajax({
type: "POST",
url: "GenerateMatrix.aspx/GenerateExcel",
data: jsonText,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
},
failure: function (response) {
alert(response.d);
}
});
}
and this is the web method definition:
[System.Web.Services.WebMethod()]
public static string GenerateExcel(List<string> list)
{
HttpContext.Current.Response.AppendHeader("content-disposition", "attachment;filename=FileEName.xls");
HttpContext.Current.Response.Charset = "";
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
HttpContext.Current.Response.Write(list[0]);
HttpContext.Current.Response.End();
return "";
}
How to get it done?
One more thing: I want to download it on client PC, not to save it on server.
well i have done it using iframe
this is the modified ajax function call
function GenerateExcel() {
var ResultTable = jQuery('<div/>').append(jQuery('<table/>').append($('.hDivBox').find('thead').clone()).append($('.bDiv').find('tbody').clone()));
var list = [$(ResultTable).html()];
var jsonText = JSON.stringify({ list: list });
$.ajax({
type: "POST",
url: "GenerateMatrix.aspx/GenerateExcel",
data: jsonText,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
if (isNaN(response.d) == false) {
$('#iframe').attr('src', 'GenerateMatrix.aspx?ExcelReportId=' + response.d);
$('#iframe').load();
}
else {
alert(response.d);
}
},
failure: function (response) {
alert(response.d);
}
});
}
and this is the design part
<iframe id="iframe" style="display:none;"></iframe>
on Page load my code looks like this
Response.AppendHeader("content-disposition", "attachment;filename=FileEName.xls");
Response.Charset = "";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.ContentType = "application/vnd.ms-excel";
Response.Write(tableHtml);
Response.End();
Add these in your view page-
<iframe id="iframe" style="display:none;"></iframe>
<button id="download_file">Download</button>
Server side
public string Download(string file)
{
string filePath = Server.MapPath(System.Configuration.ConfigurationManager.AppSettings["FileManagementPath"]);
string actualFilePath = System.IO.Path.Combine(filePath, file);
HttpContext.Response.ContentType = "APPLICATION/OCTET-STREAM";
string filename = Path.GetFileName(actualFilePath);
String Header = "Attachment; Filename=" + filename;
HttpContext.Response.AppendHeader("Content-Disposition", Header);
HttpContext.Response.WriteFile(actualFilePath);
HttpContext.Response.End();
return "";
}
Add this code in your JavaScript
<script>
$('#download_file').click(function(){
var path = 'e-payment_format.pdf';//name of the file
$("#iframe").attr("src", "/FileCabinet/Download?file=" + path);
});
</script>
That should work!
Assuming the C# code responds with the correct headers for Excel, you can simply redirect to the link instead of using ajax:
var list = [$(ResultTable).html()];
var url = "GenerateMatrix.aspx/GenerateExcel";
var data = {list: list};
url += '?' + decodeURIComponent($.param(data));
// if url is an excel file, the browser will handle it (should show a download dialog)
window.location = url;

Categories

Resources