Server cannot append header after HTTP headers have been sent - c#

Below shown is my code after creating the CSV file, i want to download the file so i am using below code. but its throwing error " Server cannot append header after HTTP headers have been sent" at "Response.AddHeader("Content-disposition", "attachment; filename=" + fileCSV + "\"");" place. Its downloading but browser not redirecting to same page.
string[] header = { "Error Occurred On", "Controller Name", "Action Name", "Exception Occurred", "Stack Trace Description", "InnerException Occurred", "Stack Trace InnerException Occurred " };
The code:
DataTable dt = new DataTable();
for (int e = 0; e < header.Length; e++)
{
dt.Columns.Add(header[e], typeof(string));
}
StringBuilder sb = new StringBuilder();
IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().Select(column => column.ColumnName);
sb.AppendLine(string.Join(",", columnNames));
dt.DefaultView.RowFilter = "[Exception Occurred] LIKE '%" + keyword + "%'";
DataTable dtFilter = new DataTable();
dtFilter = dt.DefaultView.ToTable();
foreach (DataRow row in dtFilter.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(",", fields));
}
System.IO.File.WriteAllText(fileCSV, sb.ToString());
byte[] bytes = Encoding.ASCII.GetBytes(sb.ToString());
if (bytes != null)
{
Response.Clear();
Response.ContentType = "text/csv";
//Response.AddHeader("Content-Length", bytes.Length.ToString());
Response.AddHeader("Content-disposition", "attachment; filename=" + fileCSV);
Response.BinaryWrite(bytes);
Response.Flush();
Response.End();
}

You cannot redirect after downloading the File, you are attempting to perform 2 actions where you can only do the first.
I suggest that you download the file in a new (popup) window and redirect the main page if required.
Edit:-
You could force the download by opening the file-download action using window.open.
Example:-
Download File
<script>
$(function() {
$('a.file-download').click(function() {
window.open($(this).data('file'));
});
});
</script>

In HTTP there is a single response for each request. So this error means that you've already send something to response.

Not sure you are still looking for an answer, but rather than Response.Flush & Response.End, try HttpContext.ApplicationInstance.CompleteRequest(). It has solved a lot of my problems when trying to write directly to request stream.

Some few minutes ago I had the the same problem with a file download. I want to share what worked for me.
The most important thing add:
Response.ClearHeaders();
in the begin of action result.
And, to be honest, I read it here https://www.codeproject.com/Questions/1038819/How-to-resolve-this-error-Server-cannot-append-hea

Related

Display or Download document from Ajax call in MVC C# Application

I am attempting to download an SSRS report from a MVC C# application. I have an HttpResponseBase object and am sending it back to the client to be displayed/downloaded by the user, however, when the response is sent back to the client, I am getting symbols only.
Can some one please help clarify what I am missing upon the success of the ajax call?
I have attempted to use an option which worked with some success, however, the URL is too long when selecting certain parameters.
My controller is as follows:
[HttpPost]
public ActionResult DownloadReportData(string reportName, string reportPath, string reportParams, string repID, string book, string exportFormat)
{
string extension = string.Empty;
string fileName = reportName + "_" + (book.Length > 0 ? "" + book + "_" : "") + repID;
Dictionary<string, string> dc = new Dictionary<string, string>();
if (reportParams.Length > 0)
{
string[] param = reportParams.Split(';');
int i = 0;
while (i < param.Length)
{
dc.Add(param[i].Split('=')[0], param[i].Split('=')[1]);
i += 1;
}
}
//PDF for pdf export, EXCEL for excel export
byte[] bytes = autil.Reporting.ReportsManager.GenerateReport(reportPath, dc, exportFormat);
Response.ClearContent();
if (exportFormat.ToLower() == "pdf")
{
Response.ContentType = "application/pdf";
extension = ".pdf";
//Response.AppendHeader("Content-Disposition", "inline;filename=" + fileName + "" + extension + "");
Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName + "" + extension + "");
}
else if (exportFormat.ToLower() == "excel")
{
//Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.ContentType = "application/vnd.ms-excel";
extension = ".xls";
Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName + "");
}
//Response.AppendHeader("Content-Disposition", "inline;filename=" + fileName + "" + extension + "");
Response.BufferOutput = true;
Response.AddHeader("Content-Length", bytes.Length.ToString());
Response.BinaryWrite(bytes);
Response.End();
//return null;
return Json(Response);
}
My view is as follows:
<div id="target"></div>
My javascript (ajax call) is as follows:
<script>
$.ajax({
type: 'POST',
url: '#Url.Action("DownloadReportData", "Reports")',
data:
{
reportName: reportName,
reportPath: reportPath,
reportParams: reportParams,
book: bookCode,
exportFormat: exportType
}
, success: function (data) {
$("#target").append($("<iframe>", {
srcdoc: data
}));
if (data.error == "none") {
alert("success" + data);
}
}
});
</script>
I am expecting either a downloaded version of the report or a prompt to Open (inline) or Save As to appear once the call is completed.
I am currently experiencing the following to be displayed:
%PDF-1.3 1 0 obj [/PDF /Text /ImageB /ImageC /ImageI] endobj 5 0 obj << /Length 1968 /Filter /FlateDecode >> stream x��[]o7}G��DZ��{�7��REi�H�Z�!�R�]�I������kdz��i#D���{αϽ���9��7h��|5�SM6�Y��Ҍt�t�J���4�DkC�$����9������.{�����y�� =����'�������Q�j ���]��]^����E���gG�^M�c�ʨ��/�A(�<�����a��"�>y�����b/��Ś�
I faced the same issue when I was trying to download excel from my ajax call. After many days I got the answer that you can't (I read it somewhere on SO). So I converted my ajax call to this
<div class="col-md-3">
#Html.ActionLink("Export Report", "ExportExcel", "Training",
new { }, new { #class = "btn btn-danger" })
</div>
and after that my code worked. You might have to make a few changes in order for this to work. I hope this help.

Creating a dialog for saving a csv file with C# and asp.net

Within my code I have an asp repeater and I wish to allow users to export the data from this to a csv file. Exporting to a csv works fine but when I wish to show a dialog box for users to choose where to save to nothing happens. I have went through several different solutions that apparently work but when I run them in my code nothing happens and I can not figure out why. This is my code at the moment:
protected void exportCSV_Click(object sender, EventArgs e)
{
try
{
string FilePath = Server.MapPath("~") + "\\test.csv";
StringBuilder columnbind = new StringBuilder();
foreach (Control item in rpt_bookings.Items)
{
Literal row1 = (Literal)item.FindControl("ltl_bookingemail");
Literal row2 = (Literal)item.FindControl("ltl_bookingphone");
Literal row3 = (Literal)item.FindControl("ltl_bookingcost");
string fullRow = row1.Text.ToString() + "," + row2.Text.ToString() + "," + row3.Text.ToString();
columnbind.Append(fullRow);
columnbind.Append("\r\n");
}
//// Creates the file on server
File.WriteAllText(FilePath, columnbind.ToString());
string FileName = "test.csv";
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
response.Clear();
response.ContentType = "text/csv";
response.AddHeader("Content-Disposition", "attachment; filename=" + FileName + ";");
response.TransmitFile(FilePath);
response.Flush();
response.End();
//// Deletes the file on server
File.Delete(FilePath);
//response.End();
lblmsg.Text = "";
}
catch (Exception ex)
{
Console.Write(ex);
Debug.Write(ex);
lblmsg.Text = ex.Message;
lblmsg.Style.Add("color", "#c73939");
}
}
The csv is correctly created but nothing happens with the response. The code executes but nothing comes up on the screen at all. According to several other questions along the same lines this is the solution.
Try the following changes:
response.ContentType = "application/octet-stream";
response.AppendHeader("Content-Disposition","attachment; filename=" + Filename + ";");
response.TransmitFile( FilePath );
response.End();
Don't delete the file in the next line but in the finally block instead:
catch (Exception ex)
{
[..]
}
finally
{
File.Delete(FilePath);
}

PageRequestManagerParserErrorException when attempting to send CSV data

In C# I'm having trouble with printing out the csv file
As of right now it is just throwing an error, it won't print the file
The error is:
Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed.
Here is my code behind, thanks for any help, I think I'm doing it right though
protected void csvbutton_Click(object sender, EventArgs e)
{
string filename = App_Name_L.ToString();
StringBuilder sb = new StringBuilder();
string[] columnNames = new string[]
{
"APPLICATION NAME",
"DESCRIPTION",
"APPLICATION OWNER",
"TSO",
"RESPONSIBLE MANAGER",
"SIGN OFF",
"CO-EXISTENCE STATUS",
"CO-EXISTENCE PROGRESS",
"MIGRATION PHASE",
"NEXT STEPS",
"LAST UPDATE"
};
sb.AppendLine(string.Join(",", columnNames));
sb.AppendLine(Environment.NewLine);
string[] fields = new string[]
{
App_Name_L.Text.ToString(),
Description_L.Text.ToString(),
App_Owner_L.Text.ToString().Replace(",", "."),
TSO_L.Text.ToString().Replace(",", "."),
Responsible_Manager_L.Text.ToString().Replace(",", "."),
Sign_Off_L.Text.ToString().Replace(",", "."),
this.status_export(0, Convert.ToInt16(CS_H.Value.ToString())),
this.status_export(1, Convert.ToInt16(CP_H.Value.ToString())),
Migration_Phase_L.Text.ToString(),
Next_Steps_L.Text.ToString().Replace(",", "."),
Last_Update_L.Text.ToString()
};
sb.AppendLine(string.Join(",", fields));
stringout.Text = sb.ToString();
Response.Clear();
Response.ContentType = "text/csv";
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + ".csv\"");
Response.Write(sb.ToString());
Response.End();
}
Joel this is something that I have just tested and it works in my environment look at this and see if you can apply it to your code as well replace where I have data with your sb.Tostring();
private static void ExportToExcel(string data)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=\"" + filename + ".csv\"");
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("UTF-8");
HttpContext.Current.Response.ContentType = "text/csv";
HttpContext.Current.Response.Write(data);
HttpContext.Current.Response.End();
}

Response.WriteFile writing the content twice

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.....

Export to CSV using MVC, C# and jQuery

I am trying to export a list to a CSV file.
I got it all working up to the point I want to write to file to the response stream. This doesn't do anything.
Here is my code:
Call the the method from the page.
$('#btn_export').click(function () {
$.post('NewsLetter/Export');
});
The code in the controller is as follows:
[HttpPost]
public void Export()
{
try
{
var filter = Session[FilterSessionKey] != null ? Session[FilterSessionKey] as SubscriberFilter : new SubscriberFilter();
var predicate = _subscriberService.BuildPredicate(filter);
var compiledPredicate = predicate.Compile();
var filterRecords = _subscriberService.GetSubscribersInGroup().Where(x => !x.IsDeleted).AsEnumerable().Where(compiledPredicate).GroupBy(s => s.Subscriber.EmailAddress).OrderBy(x => x.Key);
ExportAsCSV(filterRecords);
}
catch (Exception exception)
{
Logger.WriteLog(LogLevel.Error, exception);
}
}
private void ExportAsCSV(IEnumerable<IGrouping<String, SubscriberInGroup>> filterRecords)
{
var sw = new StringWriter();
//write the header
sw.WriteLine(String.Format("{0},{1},{2},{3}", CMSMessages.EmailAddress, CMSMessages.Gender, CMSMessages.FirstName, CMSMessages.LastName));
//write every subscriber to the file
var resourceManager = new ResourceManager(typeof(CMSMessages));
foreach (var record in filterRecords.Select(x => x.First().Subscriber))
{
sw.WriteLine(String.Format("{0},{1},{2},{3}", record.EmailAddress, record.Gender.HasValue ? resourceManager.GetString(record.Gender.ToString()) : "", record.FirstName, record.LastName));
}
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=adressenbestand.csv");
Response.ContentType = "text/csv";
Response.Write(sw);
Response.End();
}
But after Response.Write(sw) nothing is happening. Is it even possible to save a file this way?
Regards
Edit
The response headers I see when I click the button are:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/csv; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNetMvc-Version: 2.0
Content-Disposition: attachment; filename=adressenbestand.csv
X-Powered-By: ASP.NET
Date: Wed, 12 Jan 2011 13:05:42 GMT
Content-Length: 113
Which seem OK to me..
Edit
I got rid of the jQuery part en replaced it by an hyperlink and this is working fine for me now:
<a class="export" href="NewsLetter/Export">exporteren</a>
yan.kun was on the right track but this is much much easier.
public FileContentResult DownloadCSV()
{
string csv = "Charlie, Chaplin, Chuckles";
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "Report123.csv");
}
With MVC you can simply return a file like this:
public ActionResult ExportData()
{
System.IO.FileInfo exportFile = //create your ExportFile
return File(exportFile.FullName, "text/csv", string.Format("Export-{0}.csv", DateTime.Now.ToString("yyyyMMdd-HHmmss")));
}
In addition to Biff MaGriff's answer. To export the file using JQuery, redirect the user to a new page.
$('#btn_export').click(function () {
window.location.href = 'NewsLetter/Export';
});
What happens if you get rid of the stringwriter:
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=adressenbestand.csv");
Response.ContentType = "text/csv";
//write the header
Response.Write(String.Format("{0},{1},{2},{3}", CMSMessages.EmailAddress, CMSMessages.Gender, CMSMessages.FirstName, CMSMessages.LastName));
//write every subscriber to the file
var resourceManager = new ResourceManager(typeof(CMSMessages));
foreach (var record in filterRecords.Select(x => x.First().Subscriber))
{
Response.Write(String.Format("{0},{1},{2},{3}", record.EmailAddress, record.Gender.HasValue ? resourceManager.GetString(record.Gender.ToString()) : "", record.FirstName, record.LastName));
}
Response.End();
Respect to Biff, here's a few tweaks that let me use the method to bounce CSV from jQuery/Post against the server and come back as a CSV prompt to the user.
[Themed(false)]
public FileContentResult DownloadCSV()
{
var csvStringData = new StreamReader(Request.InputStream).ReadToEnd();
csvStringData = Uri.UnescapeDataString(csvStringData.Replace("mydata=", ""));
return File(new System.Text.UTF8Encoding().GetBytes(csvStringData), "text/csv", "report.csv");
}
You'll need the unescape line if you are hitting this from a form with code like the following,
var input = $("<input>").attr("type", "hidden").attr("name", "mydata").val(data);
$('#downloadForm').append($(input));
$("#downloadForm").submit();
From a button in view call .click(call some java script). From there call controller method by window.location.href = 'Controller/Method';
In controller either do the database call and get the datatable or call some method get the data from database table to a datatable and then do following,
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
//Build the CSV file data as a Comma separated string.
string csv = string.Empty;
foreach (DataColumn column in dt.Columns)
{
//Add the Header row for CSV file.
csv += column.ColumnName + ',';
}
//Add new line.
csv += "\r\n";
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn column in dt.Columns)
{
//Add the Data rows.
csv += row[column.ColumnName].ToString().Replace(",", ";") + ',';
}
//Add new line.
csv += "\r\n";
}
//Download the CSV file.
Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=SqlExport"+DateTime.Now+".csv");
Response.Charset = "";
//Response.ContentType = "application/text";
Response.ContentType = "application/x-msexcel";
Response.Output.Write(csv);
Response.Flush();
Response.End();
}
Even if you have resolved your issue, here is another one try to export csv using mvc.
return new FileStreamResult(fileStream, "text/csv") { FileDownloadName = fileDownloadName };
I Think you have forgot to use
Response.Flush();
under
Response.Write(sw);
please check

Categories

Resources