I know this question was asked & answered before, but the solutions don't work for me. I have to dynamically create a list of PDFs, and each row has a checkbox. You check the PDFs you want to download, whose ID get passed to a function to create the PDF. I store those PDF's in a list, which gets passed to the Zip function (DotNetZip). Problem is, when first generating a PDF, I get that error somewhere in the middle of creating it, about halfway through, when adding a new page in the PDF. Usually it's the same spot but occasionally it changes where it crashes. Could anyone look at my code and point out where I'm messing up?
protected void Download_PDF_Click(object sender, EventArgs e)
{
Response.Clear();
Response.BufferOutput = false;
HttpContext c = HttpContext.Current;
String archiveName = "Arhiva Inspectii.zip";
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=\"" + archiveName + "\"");
int nr_rows = tbl_inspectii.Rows.Count - 1;
foreach (String id_chbx in ID_Checkbox)
{
CheckBox chbx = tbl_inspectii.FindControl(id_chbx) as CheckBox;
String PDF_id = "";
if(chbx != null)
{
if(chbx.Checked)
{
PDF_id = chbx.ID.Replace("ChBx", string.Empty);
Create_PDF(PDF_id);
}
}
}
string destdir = Server.MapPath("~/Zip/") + archiveName;
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(PDF_list, destdir);
zip.Save(destdir);
}
}
protected byte[] Create_PDF(String id_insp_max)
{
using (MemoryStream ms = new MemoryStream())
{
Document brosura = new Document(PageSize.A4);
brosura.SetMargins(40, 40, 40, 40);
PdfWriter wri = PdfWriter.GetInstance(brosura, ms);//new FileStream(Server.MapPath("Pdf/") + titlu_pdf + ".pdf", FileMode.Create)
HttpContext.Current.Response.AppendHeader("Content-Disposition", "inline; filename=\"" + titlu_pdf + ".pdf\"");
PdfWriter.GetInstance(brosura, HttpContext.Current.Response.OutputStream);
brosura.Open();
//lots of SQL, and brosura.Add();
//at some point, a brosura.Add() has the error halfway through the pdf
brosura.Close();
PDF_list.Add(titlu_pdf);
wri.Close();
return ms.ToArray();
}
}
Try to move the Response block after you closed the Document.
And try to add Response.End();
And why are you return the Array in Create_PDF but does not using it?
Related
I tried to export the dataset which have 2 tables to the excel sheet, unfortunately I can't.
I have code to export data table to excel. So instead of dataset, I called the ExportToExcel function which I have in my code to export datatable to excel 4 times. But once it created the first sheet, it stops the control flow. Control doesn't call the second function
ExportToExcel(getReports.Tables[1], "ConsumerBookedSlots");
Here is the code:
public ActionResult GetCuratorsAvailability(string availabilitydate)
{
string fromDate = "";
string endDate = "";
if (availabilitydate != "")
{
fromDate = availabilitydate.Split('-')[0];
endDate = availabilitydate.Split('-')[1];
if (DateTime.Parse(endDate) >= DateTime.Parse(fromDate))
{
DataSet getReports = AdminBLL.GetCuratorsAvailability(fromDate, endDate);
ExportToExcel(getReports.Tables[0], "CuratorsAvailableSlots");
ExportToExcel(getReports.Tables[1], "ConsumerBookedSlots");
}
}
return View("Reports");
}
public void ExportToExcel(DataTable dt, string FileName)
{
if (dt.Rows.Count > 0)
{
if (System.Web.HttpContext.Current.Response.RedirectLocation == null)
{
string filename = FileName + ".xls";
System.IO.StringWriter tw = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter hw = new System.Web.UI.HtmlTextWriter(tw);
DataGrid dgGrid = new DataGrid();
dgGrid.DataSource = dt;
dgGrid.DataBind();
//Get the HTML for the control.
dgGrid.RenderControl(hw);
//Write the HTML back to the browser.
//Response.ContentType = application/vnd.ms-excel;
Response.AppendHeader("Content-Disposition", "attachment; filename=" + filename + "");
Response.ContentType = "application/vnd.ms-excel";
Response.Write(tw.ToString());
Response.Flush();
Response.End();
}
}
}
I am unable to download getReports.Tables[1] data because I am getting this error:
server cannot append header after http headers have been sent. mvc
And it is downloading firstfile after error in the browser.
After the download of the first file, the execution hits this line-
Response.End();
That means, the response is ended, and headers have been sent to the client. There's no way you can initiate the download of the second file. If you want to download multiple files in a single button click, you need to zip it into one, and then initiate the download.
To zip the files you can do this-
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath);
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
Snippet from here.
If you want to make both excel spreadsheets a part of single excel document by putting each one of them as a worksheet, you can use ClosedXML.
I've used ClosedXML (an OpenXML implementation) several times, including in an ASP.NET MVC application and it works like a charm.
Exporting data is a breeze:
using (var memoryStream = new MemoryStream())
{
using (XLWorkbook workbook = new XLWorkbook())
{
using (IXLWorksheet worksheet = workbook.AddWorksheet("WorksheetName"))
{
var toExport = GetData();
worksheet.Row(1).Style.Font.Bold = true;
worksheet.Cell(1, 1).Value = "Column 1";
worksheet.Cell(1, 2).Value = "Column 2";
worksheet.Cell(1, 3).Value = "Column 3";
// Export the data and set some properties
worksheet.Cell(2, 1).Value = toExport.AsEnumerable();
worksheet.RangeUsed().SetAutoFilter();
worksheet.Columns().AdjustToContents();
workbook.SaveAs(memoryStream);
memoryStream.Position = 0;
return File(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "filename.xlsx");
}
}
}
I'm working on MVC5 project on .NET 4.5.3. I have View with #Html.BeginForm and FormMethod.Post this view calls [HttpPost] ActionResult. In the Controller i get necessary ID's from the submitted form and then pass them for exporting.
[HttpPost]
public ActionResult PrepareForExport()
{
ExportService export = new ExportService();
if (Request.Form["button"] != null)
{
string selected = Request.Form["button"].ToString();
export.GeneratePdf(ptoRequestsService.GetByID(Convert.ToInt32(selected)));
}
else if (Request.Form["toExport"] != null)
{
List<PtoRequest> ptoListForExport = new List<PtoRequest>();
string selectedItems = Request.Form["toExport"].ToString();
string[] selectedList = selectedItems.Split(',');
foreach (var pto in selectedList)
{
ptoListForExport.Add(ptoRequestsService.GetByID(Convert.ToInt32(pto)));
}
export.GenerateZip(ptoListForExport);
}
return RedirectToAction("Requests" + LoggedUser.ID);
}
And in the ExportService class i have this export method.
public void GenerateZip(List<PtoRequest> approvedPtos)
{
byte[] pdfContent = null;
string dateFormat = "yyyy-MM-dd";
string filePath = null;
if (!Directory.Exists(HttpContext.Current.Server.MapPath(#"~/Files/PdfFiles/")))
{
Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/Files/PdfFiles/"));
filePath = HttpContext.Current.Server.MapPath("~/Files/PdfFiles/");
}
else
{
filePath = HttpContext.Current.Server.MapPath("~/Files/PdfFiles/");
}
foreach (var Pto in approvedPtos)
{
pdfContent = FillPdfTemplate(Pto);
string fileName = Pto.User.FirstName + " " + Pto.User.LastName + "_" + Pto.StartDate.ToString(dateFormat) + ".pdf";
string fileDirectory = filePath + fileName;
using (FileStream fs = new FileStream(fileDirectory, FileMode.OpenOrCreate))
{
fs.Write(pdfContent, 0, pdfContent.Length);
}
}
string zipName = String.Format("Report_{0}.zip", DateTime.Now.ToString("yyyy-mm-dd-HHmmss"));
string zipFile = filePath + zipName;
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(filePath);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.BufferOutput = false;
HttpContext.Current.Response.ContentType = "application/zip";
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + zipName);
zip.Save(HttpContext.Current.Response.OutputStream);
HttpContext.Current.Response.Write(zip.ToString());
HttpContext.Current.Response.End();
Directory.Delete(filePath, true);
}
}
Everything works fine, but when my method complete the job i got exception with code 500. I do my googling to understand my problem and it is something like this 1. I understand that the problem is in HttpHeader but did not understand how to solve it in my case. Then i tried the solution with if (!Response.IsRequestBeingRedirected) but i still got this exception. After this I tried to return ZipFile from GenerateZip Method and instead calling Response in ExportService class to call it at Controller, but i still got this Error. I have an idea to remove [HttpPost] attribute and to take my ID's in other way but my point is to do it that way. Can anyone point me in any direction to solving this? What is the right decision, where should i call Response for this case, is it option to write jQuery script to prevent submitting form in .cshtml?
You are calling HttpContext.Current.Response.End(); in your GenerateZip method and then attempting to Redirect the user in the ControllerAction. This is causing your "Headers cannot be written after response is sent" error. Instead of that, return a byte array or Stream object (FileStream?) from the GenerateZip method and return a FileResult in the Action. Redirecting the user after the file download can (only?) be achieved using JavaScript. Here is an example. How to return a FileResult AND RedirectToAction in ASP.NET MVC
I have classic asp.net website. I have a datagrid that i would like to export as excel sheet. The site uses windows authentication.
I picked up the code while googling for a solution. Here is what i am experiencing in FF and Chrome and need help to rectify it:
some times the file download will happen without any issue, but if i keep the window inactive for a minute or so and then try again, eiter i get page not found issue or the browser will keep on asking for login (kind of stuck in a loop).
some times when the export button gets clicked, FF just keeps on displaying windows login popup, like it is stuck in a loop.
Button:
<asp:Button ID="cmdExportDataGridToExcel_Click" runat="server" OnClick="cmdExportToExcel_Click" CssClass="StandardText" Text="Export to Excel" Width="120px" Height="33px"></asp:Button>
Code:
protected void cmdExportDataGridToExcel_Click(object sender, System.EventArgs e)
{
//export to excel
Response.Clear();
var userName = CommonMethods.GetUserId();
var fileName = string.Format("ScheduledOrdersReport-{0}-{1:yyyyMMddhhmmsstt}.xls", userName, DateTime.Now);
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.ContentType = "application/vnd.ms-excel";
Response.Charset = "";
hsGrid.AllowPaging = false;
this.BindHSGrid(false, string.Empty);
//this.EnableViewState = false;
var oStringWriter = new StringWriter();
var oHtmlTextWriter = new HtmlTextWriter(oStringWriter);
this.ClearControls(hsGrid);
hsGrid.RenderControl(oHtmlTextWriter);
Response.Write(oStringWriter.ToString());
Response.End();
}
private void ClearControls(Control control)
{
for (int i = control.Controls.Count - 1; i >= 0; i--)
{
ClearControls(control.Controls[i]);
}
if (!(control is TableCell))
{
if (control.GetType().GetProperty("SelectedItem") != null)
{
LiteralControl literal = new LiteralControl();
control.Parent.Controls.Add(literal);
try
{
literal.Text = (string)control.GetType().GetProperty("SelectedItem").GetValue(control, null);
}
catch
{
}
control.Parent.Controls.Remove(control);
}
else if (control.GetType().GetProperty("Text") != null)
{
LiteralControl literal = new LiteralControl();
control.Parent.Controls.Add(literal);
literal.Text = (string) control.GetType().GetProperty("Text").GetValue(control, null);
control.Parent.Controls.Remove(control);
}
}
}
Update 1
I have tested it by putting the data in a session and then creating an excel from the session. It also is resulting in the same error.
var dv = (DataView) Session["ScheduledOrdersReport"];
Response.Clear();
var userName = CommonMethods.GetUserId();
var fileName = string.Format("ScheduledOrdersReport-{0}-{1:yyyyMMddhhmmsstt}.xls", userName, DateTime.Now);
Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
Response.Charset = "";
Response.ContentType = "application/vnd.ms-excel"; //"application/vnd.xls";
var stringWrite = new StringWriter();
var htmlWrite = new HtmlTextWriter(stringWrite);
var g = new GridView();
g.DataSource = dv;
g.DataBind();
g.RenderControl(htmlWrite);
Response.Write(stringWrite.ToString());
Response.End();
Update 2
I just did another test by opening a popup and then using update 1 code on it. No issues, on click or keeping the page for some time and then clicking the button again.
Looks like it is the post back and response clear and end issue.
I just simply can't find anything at this time to fix this properly. May be i need a little break.
What about to start a webclient?
and set the NetworkCredentials? Then you must'nt login every time...
like:
`using(Webclient webclient = new Webclient())
{
webClient http = new Webclient();
http.Credentials = new NetworkCredential("YourUsername", "YourPassword");
}
`
Gurus
We are developing a win forms application in .NET 4.0, that generate PDF reports using RDLC control. As the report generation takes lot of time, we decided to implement Parallel for each.. With the following code , it generates the PDF for first record after that, system just hangs.. Do help us...
public void generatereport()
{
button1.Enabled = false;
button4.Enabled = false;
DataTable dtBranch = new DataTable();
dtBranch = getBranchNo(); -- we might get around 300 rows here
try
{
Parallel.ForEach(dtBranch.AsEnumerable(), drow =>
{
// Shows the ReportData along with the branch code
reportdate = drow["reportdate"].ToString();
string branchName = drow["BranchNo"].ToString();
ProcessReport(branchName);
});
reportViewer2.Visible = false;
button1.Enabled = true;
button4.Enabled = true;
lblMsg.Text = "Branch Summary Generated at '" + #fullpath + "'";
}
catch (AggregateException e)
{
//Console.Write(e.Data + e.Message);
//Console.ReadLine();
}
}
private void ProcessReport(string branName)
{
if (reportViewer2.InvokeRequired)
{
ProcessReportCallBack d = new ProcessReportCallBack(ProcessReport);
Invoke(d, new object[] { branName });
}
else
{
log.Debug("branch" + DateTime.Now.ToString());
DataTable dt = new DataTable();
dt = getReportOrderNoBasedonBranchID(branName);//Get all the report order no as per the branch id
this.sp_getReportOrderNoTableAdapter.Fill(this.getRptNo.sp_getReportOrderNo, branName);
this.reportViewer2.LocalReport.SubreportProcessing += new Microsoft.Reporting.WinForms.SubreportProcessingEventHandler(this.reportViewer2_Subreport1);
this.reportViewer2.Clear();
this.reportViewer2.ProcessingMode = ProcessingMode.Local;
this.reportViewer2.LocalReport.ReportPath = #"Master.rdlc";
this.reportViewer2.Refresh();
Savepdf("reportViewer2", branName + "_" + reportdate);
}
}
In save PDF we are generating the PDF report..
byte[] bytes = null;
bytes = reportViewer2.LocalReport.Render(
"PDF", null, out mimeType, out encoding,
out extension,
out streamids, out warnings);
filename = BranchName + '_' + "Summary" + ".pdf";
using (FileStream fs = new FileStream(#fullpath + '\\' + filename, FileMode.Create))
{
fs.Write(bytes, 0, bytes.Length);
fs.Close();
}
bytes = null;
Kindly provide your feedback
you don't need a sample codes to Export to PDF, when the RDLC is already rendered to the ReportViewer try to find this icon from the ToolBars of ReportViewer Control:
EDIT:
Save RDLC reports as PDF programaticaly
public void Save(ReportViewer viewer, string savePath)
{
byte[] Bytes = viewer.LocalReport.Render("PDF", "", null, null, null, null, null);
using (FileStream Stream = new FileStream(savePath, FileMode.Create)) {
Stream.Write(Bytes, 0, Bytes.Length);
}
}
Here is my code..
string attachment = "attachment; filename=Call-Details-Report-" + startDate.SelectedDate.Value.ToString("MM-dd-yyyy") + ".csv";
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.AddHeader("Content-Disposition", attachment);
Response.ContentType = "text/csv";
Response.AddHeader("Pragma", "public");
Response.WriteFile(downloadLocation+"\\"+fileName);
Response.End();
I'm using the above code to download a csv file from a location.But surprisingly the contents gets written twice or some times thrice into the file that i download though actually it isn't so with the file on server.I'm writing my code in c#.
The above piece of code works perfectly in local machine,but the issue is in Production server.
Here is my complete Method
private void DownloadReport(string query)
{
string downloadFolderPath = "";
string filePath = "";
string dbAndApplicationServerStatus = ConfigurationManager.AppSettings["SameDBAndApplicationServer"] != null ? ConfigurationManager.AppSettings["SameDBAndApplicationServer"] : "1";
if (dbAndApplicationServerStatus == "0")
{
Log.Write("So the DB And Application are on differrent servers,hence trying to read Download folder path on DB Server....");
downloadFolderPath = ConfigurationManager.AppSettings["ReportDownloadLocation"] != null ? ConfigurationManager.AppSettings["ReportDownloadLocation"] : "-1";
Log.Write("Download Path is " + downloadFolderPath);
}
else
{
Log.Write("So the DB and Application and Db are on same server......");
downloadFolderPath = Server.MapPath("Download");
downloadFolderPath = downloadFolderPath.Replace("\\", "//");
if (!Directory.Exists(downloadFolderPath))
{
Directory.CreateDirectory(downloadFolderPath);
}
Log.Write("Download Path is " + downloadFolderPath);
}
string status="";
StringBuilder headerQuery = new StringBuilder();
StringBuilder rowQuery = new StringBuilder();
StringBuilder sqlQuery = new StringBuilder();
filePath = downloadFolderPath;
string folderName = DateTime.Now.ToString("MM-dd-yyyy");
string timeStamp = DateTime.Now.ToString("MM-dd-yy-HH-mm-ss");
string fileName = "Call-Details-Report-" + startDate.SelectedDate.Value.ToString("MM-dd-yyyy") + "_" + timeStamp + ".csv";
filePath = filePath + "/" + fileName;
bool commaRequired = false;
sqlQuery.Append("SELECT * INTO OUTFILE '");
sqlQuery.Append(filePath);
sqlQuery.Append("' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' FROM (");
headerQuery.Append("Select ");
rowQuery.Append("(Select ");
#region Creating Query
/*Sql Query is Created in this region*/
#endregion
if (!CdrSearch.WriteReportToFile(sqlQuery.ToString(),out status))
{
Log.Write("Failed to generate the file to download......");
WebPagesHelper.ShowMessage(ref lblMessage, WebPagesHelper.MessageType.Message, status);
}
else
{
Log.Write("Succesfully generated file to Download");
string downloadLocation = Server.MapPath("Download");
if (dbAndApplicationServerStatus == "0")
{
WebClient webClient = new WebClient();
string path = ConfigurationManager.AppSettings["DownloadURL"] != null ? ConfigurationManager.AppSettings["DownloadURL"].ToString() : "";
if (!Directory.Exists(downloadLocation))
{
Directory.CreateDirectory(downloadLocation);
}
if (File.Exists(downloadLocation + "\\" + fileName))
{
File.Delete(downloadLocation + "\\" + fileName);
}
webClient.DownloadFile(path + fileName, downloadLocation + "\\" + fileName);
}
Log.Write("Configured Download Location on Application" + downloadLocation);
string attachment = "attachment; filename=Call-Details-Report-" + startDate.SelectedDate.Value.ToString("MM-dd-yyyy") + ".csv";
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
Response.AddHeader("Content-Disposition", attachment);
Response.ContentType = "text/csv";
Response.AddHeader("Pragma", "public");
Log.Write(downloadLocation + "\\" + fileName);
Response.WriteFile(downloadLocation+"\\"+fileName);
Response.SetCookie(new HttpCookie("DStatus", "Completed"));
Response.End();
}
}
And the above method is called only once straight away on click of a button,so no question of any looping here.
You may check what the following lines are meant for :
webClient.DownloadFile(path + fileName, downloadLocation + "\\" + fileName);
Response.WriteFile(downloadLocation+"\\"+fileName);
Give a try by commenting out one of them, if they are really doing the same.
As a safe measure, please disable the button until your download is finished.
When i tried the following code (even published on IIS), it is just downloading once, as expected.
protected void Button1_Click(object sender, EventArgs e)
{
string attachment = "attachment; filename=Call-Details-Report-" + DateTime.Now.ToString("MM-dd-yyyy") + ".txt";
Response.ContentType = "text/html";
Response.AddHeader("Content-Disposition", attachment);
Response.AddHeader("Pragma", "public");
Response.WriteFile(#"C:\test.txt");
Response.SetCookie(new HttpCookie("DStatus", "Completed"));
Response.End();
}
There's obviously something hinky going on. You've said that it works in dev but not Prod - Are you using the same server config in both environemts (ie are you using 1 server in dev but 2 in prod?)
You've potentially got 3 steps, assuming I've understood your code...
Generate Report from SQL and write it to a file
If the file is stored on a different server, download it to the web server
Serve it
So, in the more complex scenario (which I assume Production is), At what step in the process do you start to see double entries? On the server the report is generated on, on the web servers' copy or only in the client after getting it from the Webserver?
I can't see any reason why your code to serve the file to the client would be duplicating data so can only assume it's happening before there at some point.
It would be helpful if you could use Firebug/Fiddler/??? to post the exact contents of the transmission from the webserver to the client
(Incidentally, you may want to look at the System.IO.Path class for manipulating paths, it makes your code more readable and robust - No more worrying about trailing slashes!)
ok so the real culprit was Response.WriteFile in this case.In my case i guess since the size of data was quite huge Response.WriteFile was not working as expected.I found some where that in case of large file downloads, its best to use Response.TransmitFile.Left with no other option i changed my code and used Response.TransmitFile and eureka! the problem was solved.No more duplicate records in downloaded file.Though the reason is still unknown, Response.TransmitFile solved the issue.....