Blank csv file with Linq/ASP.Net/MVC - c#

Afternoon, all!
Having a bit of a problem getting my StringWriter to produce a CSV with data in it. I made some changes and added several of the entries from the database per the client. I got the file to create once before I made these changes. There is information in the database to grab. This is probably just my lack of understanding of the StringWriter and how it works.
public void ExportToCsv()
{
DateTime newDate = DateTime.Now.AddMonths(-6);
StringWriter sw = new StringWriter();
sw.WriteLine("Name,SS#,Application Date,Company,Job Code,Job Applied For,2nd Choice,3rd Choice,EEO Job Group,Gender,EEO Applicant,Veteran Status,Interview Date Personnel,Interview Date Manager,Reference Date,Physical Date,Disposition,Comments,Job Fair Location");
Response.ClearContent();
Response.AddHeader("content-disposition", "attachment;filename=ExportedAAInfo.csv");
Response.ContentType = "text/csv";
var q = _db.applicants.Where(n => n.date_applied > newDate && n != null);
foreach (var item in q)
{
string jobGroup = "Test Job";
sw.WriteLine(string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18}",
item.last_name + "," + item.first_name,
item.idnumber,
String.Format("{0:MM/dd/yyyy}", item.date_applied),
item.referral,
"",
item.job_applied,
"Any",
"Any",
jobGroup,
item.gender,
item.race,
item.veteran_status,
item.interview_date,
item.interview_manager,
item.reference_date,
item.physical_date,
item.disposition,
item.comments,
item.job_fair_location
));
}
Response.Write(sw.ToString());
Response.Flush();
Response.Close();
}
I am properly getting the file download, just nothing in it. Not even the manual WriteLine with the column headers in it. Any points you can see to point me in the right direction would be greatly appreciated!
EDIT:
I have simply narrowed everything down to just trying to print a string into the Response. I am using this little method.
public void ExportToCsv()
{
string sw = "Hello, World!";
Response.Clear();
Response.ContentType = "text/plain";
Response.AddHeader("content-disposition", "attachment;filename=ExportedAAInfo.txt");
Response.Write(sw.ToString());
Response.End();
}
Ignore the name of the method, its just testing at this point. I am still getting the file download, just nothing printed in the file.
I am wondering if there is a certain way in which you must call the method for the response to pick it up?
At the moment i am just using
#Html.Action("ExportToCsv")
as the href on my button to download. Are there any server settings or anything that I need to look into to keep this from happening? Kinda at a loss as I cant get this simplified method to work either.

Better to use optimized class for export like one
https://github.com/jitbit/CsvExport

Yeah i probably should have used something like that #Mukesh Chudasama.
This is what I ended up with after messing around and reading some docs. Works great.
public void ExportToCsv()
{
DateTime newDate = DateTime.Now.AddMonths(-6);
MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);
tw.WriteLine("Name,SS#,Application Date,Company,Job Code,Job Applied For,2nd Choice,3rd Choice,EEO Job Group,Gender,EEO Applicant,Veteran Status,Interview Date Personnel,Interview Date Manager,Reference Date,Physical Date,Disposition,Comments,Job Fair Location");
var q = _db.applicants.Where(n => n.date_applied > newDate && n != null).ToList();
foreach (var item in q)
{
string itemString = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18}",
item.last_name + "_" + item.first_name,
item.idnumber,
String.Format("{0:MM/dd/yyyy}", item.date_applied),
item.referral,
"",
item.job_applied,
"Any",
"Any",
"job Group Test",
item.gender,
item.race,
item.veteran_status,
item.interview_date,
item.interview_manager,
item.reference_date,
item.physical_date,
item.disposition,
item.comments,
item.job_fair_location
);
tw.WriteLine(itemString);
}
tw.Flush();
byte[] bytes = ms.ToArray();
ms.Close();
Response.Clear();
Response.ContentType = "text/csv";
Response.AddHeader("content-disposition", "attachment;filename=AAInfo.csv");
Response.BinaryWrite(bytes);
Response.End();
}

Related

Corrupt Excel file output using EPPlus in ASP.NET / C#

I'm trying to use EPPlus to create a report inside an ASP.NET application. I tried using the code provided into samples package but I'm having some troubles.
The following code is executed without error:
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Sample1");
_ws.Cells["A1"].Value = "COD. CONV.";
_ws.Cells["A1"].Style.Font.Bold = true;
_ws.Cells["A1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
_ws.Cells["B1"].Value = "RAGIONE SOCIALE";
_ws.Cells["B1"].Style.Font.Bold = true;
_ws.Cells["B1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
_ws.Cells["C1"].Value = "COMMERCIALE A";
_ws.Cells["C1"].Style.Font.Bold = true;
_ws.Cells["C1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
_ws.Cells["D1"].Value = "PROVINCIA";
_ws.Cells["D1"].Style.Font.Bold = true;
_ws.Cells["D1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
_ws.Cells["E1"].Value = "ZONA";
_ws.Cells["E1"].Style.Font.Bold = true;
_ws.Cells["E1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
_ws.Cells["F1"].Value = "TELEFONO";
_ws.Cells["F1"].Style.Font.Bold = true;
_ws.Cells["F1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
_ws.Cells["G1"].Value = "EMAIL";
_ws.Cells["G1"].Style.Font.Bold = true;
_ws.Cells["G1"].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
int _i = 2;
foreach (DataRow _drRow in dtAnagrafiche.Rows)
{
_ws.Cells["A"+_i.ToString()].Value = _drRow["codice"].ToString();
_ws.Cells["B"+_i.ToString()].Value = _drRow["Nome"].ToString();
_ws.Cells["C"+_i.ToString()].Value = "";
_ws.Cells["D"+_i.ToString()].Value = _drRow["Provincia"].ToString();
_ws.Cells["E"+_i.ToString()].Value = _drRow["Zona"].ToString();
_ws.Cells["F"+_i.ToString()].Value = _drRow["Telefono"].ToString();
_ws.Cells["G"+_i.ToString()].Value = _drRow["Email"].ToString();
_i++;
}
Response.BinaryWrite(_pck.GetAsByteArray());
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=Lista_Anagrafiche.xlsx");
but the resulting file cannot be opened by Microsoft office if not 'recovered', other MS Office compatibile applications (i.e. OpenOffice) cannot open the file.
I can provide the output file if needed.
https://drive.google.com/file/d/0B-lPXYt7laDrbUFKbFZEWEwxckk/view?usp=sharing
BTW I'm using the last (4.0.5) EPPlus package obtained trough nuget, and running it in ASP.NET 4.5 web appplication.
You're missing a call to Response.End(). Without this, you're sending the response with the binary payload (the .xlsx file), which is coming over correctly, then the .aspx page that you're coding this under is being sent in the payload as well. Proof here as shown in a hex editor.
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=Lista_Anagrafiche.xlsx");
Response.BinaryWrite(_pck.GetAsByteArray());
Response.End();
Should do the trick.
As an aside, I would suggest saving the file, then doing a Response.Redirect() to the URL of the file, instead, but that's unrelated to this specific issue.
EDIT: Notably, in normal circumstances, I would suggest avoiding Response.End(), but, that is the quickest way to solve the problem you've coded yourself into. I would suggest looking for better ways to serve up these files in general, as per my above suggestion to Response.Redirect() to a saved location of the file.
Try changing your code to the following notice how I am using the string.Format function to create the filename + extension
you need to declare a constant fileName. if worse comes to worse change the .xlsx to .xls
Response.Clear();
Response.ClearHeaders();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AppendHeader("Content-Disposition", string.Format("attachment;filename={0}.xlsx", fileName));
Response.BinaryWrite(_pck.GetAsByteArray());
Response.Flush();
Response.End();

nullreference exception with reponse object in export to excel function using asp.net mvc

I have got the below method that will do export to excel, My aim is when I click the link on web page I need to export the data from kendo ui grid to excel along with that asp.net MVC4 for that purpose I have written below method.....
the below method is action method that will call when I click exporttoexcel action link on view
public ActionResult ExportToExcel()
{
byte[] file;
string targetFilename = string.Format("{0}-{1}.xlsx", "Generated", "excel");
DataTable dt = common.CreateExcelFile.ListToDataTable(GetSearchDraftPRResults());
common.CreateExcelFile excelFileForExport = new CreateExcelFile();
file = excelFileForExport.CreateExcelDocumentAsStream(dt, targetFilename);
Response.Buffer = true;
return File(file, "application/vnd.ms-excel", targetFilename);
}
and the below method is for creating excel document
public byte[] CreateExcelDocumentAsStream(DataTable dt, string filename)
{
DataSet ds = new DataSet();
ds.Tables.Add(dt);
System.Web.HttpResponse Response = null;
System.IO.MemoryStream stream = new System.IO.MemoryStream();
using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook, true))
{
WriteExcelFile(ds, document);
}
stream.Flush();
stream.Position = 0;
Response.Clear();
Response.Buffer = true;
Response.Charset = "";
Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
Response.AddHeader("content-disposition", "attachment; filename=" + filename);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
byte[] data1 = new byte[stream.Length];
stream.Read(data1, 0, data1.Length);
return stream.ToArray();
}
but when I click the action link I am getting error NullReferenceexpection at this line
Response.Clear();
I am not sure why I am getting this this exception and I am using open xml dll for export to excel
I am not sure about this procedure, Is this is right way for export to excel functionality .. would any one pls guide me in correct direction ...
Would any one please help on this that would be very grateful to me
many Thanks in advance....
You really don't need to mess with Response in the CreateExcelDocumentAsStream() method. As the name implies, the only responsibility of the method is to create the Excel file and return it as a byte[] array.
It's the (MVC) Action responsibility then to set the appropriate response headers and behavior to accommodate the client (Web Browser, in this case) needs.
Also, when you return a FileResult, it takes care of setting the Http response, no Response.Clear() or Response.Buffer needed in the Action as well.

Saving Exported doc file to the disk

Is it possible to save an exported word document file using Response.Write(). Now it's showing Save/Open dialog box, once it Converted successfully. But i need to save this file to a folder. Please help me for resolve this issue.
My conversion to Doc code is appended below.
private void ExportDataSetToWordDoc()
{
try
{
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", string.Format("attachment; filename={0}", DateTime.Today.ToShortDateString().Replace("/", "").Replace("-", "") + "_" + DateTime.Now.ToShortDateString() + ".doc"));
Response.ContentType = "application/ms-word";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
tblMain.RenderControl(htw);
Response.Write(sw.ToString());
Response.End();
}
catch (ThreadAbortException ex)
{
Common.LogError(ex);
}
}
It's up to the Browser to offer the user an "open or save" option. That's what your content-disposition "attach" is encouraging the browser to do. Your other option is content-disposition "inline", where the browser will usually just call up the application (Word in this case) to open the file. See MSDN.
Sadly, the browser will not always offer the filename you specified as the default filename in the "Save As" dialog. Often it will offer the name of your web page as the default instead. Firefox at least documents this as a bug, IE seems to think it is a "feature".
I have modified my code as shown below. Now its saving the specified folder
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", string.Format("attachment; filename={0}", DateTime.Today.ToShortDateString().Replace("/", "").Replace("-", "") + "_" + DateTime.Now.ToShortDateString() + ".doc"));
Response.ContentType = "application/ms-word";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
tblMain.RenderControl(htw);
string strPath = Request.PhysicalApplicationPath + "Test.doc";
StreamWriter sWriter = new StreamWriter(strPath);
sWriter.Write(sw.ToString());
sWriter.Close();
Thanks.
You can use a stream writer (System.IO.StreamWriter) using a path.
When the stream writer will be closed, the file will be saved on at the specified path.
But, it will save on the server disk. If you want to save on the client side, you don't have other choice than ask the user where to put the file. You can't save files on a client without its approval.
var x = new System.IO.StreamWriter(path);
x.Close();

save the document created by docX into response and send it to user for downloading

I am trying to use the amazing DocX library on codeplex to create a word document.
when the user clicks a button, the document is created and I want to be able to send it to the user immediately via response.. I am doing something similar to this now:
Edited code based on suggestions
using (DocX Report = DocX.Create(string.Format("Report-{0}.doc", DateTime.Now.Ticks)))
{
Paragraph p = Report.InsertParagraph();
p.Append("Title").FontSize(30).Bold()
.Append("Sub title").FontSize(28)
.AppendLine()
.Append(DateTime.Now.Date)
;
MemoryStream ms = new MemoryStream();
Report.SaveAs(ms);
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".docx\"");
Response.ContentType = "application/msword";
Response.Write(ms);
Response.End();
}
I have tried a few variations of this.. but I am not able to achieve what I want.. Looking at this answer I can possibly save the document on the server and open with io stream.. but I want to avoid that extra step (and then I need to delete the file too)
I don't see the point of creating a file for few milli seconds.. there has to be a way to save the contents and send them to response stream.. right?
How'd I go about it?
thanks..
EDIT: my current code either throws up cannot open file (Access denied) error If I am using file stream, OR downloads an empty document file without any content (sometimes, type of response is written to document)
This code gets me an MS word document with System.IO.MemoryStream as it's content..
Okay, here is the final working solution:
For some reason, DocX library doesn't want to save to Response.OutputStream directly, so I had to save it to memory stream and write the memory stream to response, like Neil & Daniel suggested. Here's what worked for me:
MemoryStream ms = new MemoryStream()
Report.SaveAs(ms);
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".doc\");
Response.ContentType = "application/msword";
ms.WriteTo(Response.OutputStream);
Response.End();
This might be a bit late, but I found a way to get this working with FileStreamResult:
public FileStreamResult DownloadDocument()
{
using (DocX document = DocX.Create(#"Test.docx"))
{
// Insert a new Paragraphs.
Paragraph p = document.InsertParagraph();
p.Append("I am ").Append("bold").Bold()
.Append(" and I am ")
.Append("italic").Italic().Append(".")
.AppendLine("I am ")
.Append("Arial Black")
.Font(new FontFamily("Arial Black"))
.Append(" and I am not.")
.AppendLine("I am ")
.Append("BLUE").Color(Color.Blue)
.Append(" and I am")
.Append("Red").Color(Color.Red).Append(".");
var ms = new MemoryStream();
document.SaveAs(ms);
ms.Position = 0;
var file = new FileStreamResult(ms, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
{
FileDownloadName = string.Format("test_{0}.docx", DateTime.Now.ToString("ddMMyyyyHHmmss"))
};
return file;
}
}
The important bit is setting the Position of the memorystream back to 0, otherwise it appeared to be at the end, and the file was returning empty.
Try using a MemoryStream instead of a FileStream.
Your current code looks really wrong:
You are saving the report to the OutputStream of the current response and then clear that response (!)
When you do Report.SaveAs(response.OutputStream); - it already writes file contents to the output stream. You don't need to do Response.Write(response.OutputStream);
So you code should look like this:
...
Report.SaveAs(response.OutputStream);
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".doc\"");
Response.ContentType = "application/msword";
I think you've got things a little back to front and confused.
First off, clear the output, then add the headers, then write out the content.
Response.Clear();
Response.AddHeader("content-disposition", "attachment; filename=\"" + fileName + ".doc\"");
Response.ContentType = "application/msword";
// This writes the document to the output stream.
Report.SaveAs(response.OutputStream);
Response.End();
Also , if your file is a docx format file, append .docx rather than .doc to your filename.

Create text file and download

I'm trying to write to a text file in memory and then download that file without saving the file to the hard disk. I'm using the StringWriter to write the contents:
StringWriter oStringWriter = new StringWriter();
oStringWriter.Write("This is the content");
How do I then download this file?
EDIT:
It was combination of answers which gave me my solution. Here it is:
StringWriter oStringWriter = new StringWriter();
oStringWriter.WriteLine("Line 1");
Response.ContentType = "text/plain";
Response.AddHeader("content-disposition", "attachment;filename=" + string.Format("members-{0}.csv",string.Format("{0:ddMMyyyy}",DateTime.Today)));
Response.Clear();
using (StreamWriter writer = new StreamWriter(Response.OutputStream, Encoding.UTF8))
{
writer.Write(oStringWriter.ToString());
}
Response.End();
This solved for me:
MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);
tw.WriteLine("Line 1");
tw.WriteLine("Line 2");
tw.WriteLine("Line 3");
tw.Flush();
byte[] bytes = ms.ToArray();
ms.Close();
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=file.txt");
Response.BinaryWrite(bytes);
Response.End();
Instead of storing the data in memory and then sending it to the response stream, you can write it directly to the response stream:
using (StreamWriter writer = new StreamWriter(Response.OutputStream, Encoding.UTF8)) {
writer.Write("This is the content");
}
The example uses the UTF-8 encoding, you should change that if you are using some other encoding.
Basically you create an HttpHandler by implementing the IHttpHandler interface. In the ProcessRequest method you basically just write your text to context.Response. You also need to add a Content-Disposition http header:
context.Response.AddHeader("Content-Disposition", "attachment; filename=YourFileName.txt");
Also remember to set the ContentType:
context.Response.ContentType = "text/plain";
Just a small addition to the other answers. At the very end of a download I execute:
context.Response.Flush();
context.ApplicationInstance.CompleteRequest();
I learned that otherwise, the download sometimes does not complete successfully.
This Google Groups posting also notes that Response.End throws a ThreadAbortException which you could avoid by using the CompleteRequest method.
I had many issues with this. Finnaly found a solution that seems to work everytime.
In most cases the user is going to click a button for the download. At this point it is best to redirect the page back to the same spot. add a parameter in the url that you can grab and read.
example( www.somewhere.com/mypage.aspx?print=stuff)
<asp:Button ID="btn" runat="server" Text="print something" OnClick="btn_Click" />
protected void Page_Load(object sender, EventArgs e) {
if (Request["print"] == "stuff") { Print("my test content"); }
}
/* or pass byte[] content*/
private void Print(string content ){
Response.ContentType = "text/plain";
Response.AddHeader("content-disposition", "attachment;filename=myFile.txt");
// Response.BinaryWrite(content);
Response.Write(content);
Response.Flush();
Response.End();
}
protected void btn_Click(object sender, EventArgs e) {
// postbacks give you troubles if using async.
// Will give an error when Response.End() is called.
Response.Redirect(Request.Url + "?print=queue");
}
Extension of #Vinicious answer.
I had data that could contain commas. The common solution is to escape that piece of data by enclosing it in quotes, while making sure to also escape quotes that could also be a part of the data.
One rub I came against and a warning when writing CSV, excel will not like you if you put spaces trailing your commas. discovered solution to my problem from superuser answer
protected void btnDownload_Click(object sender, EventArgs e)
{
MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms, System.Text.Encoding.UTF8);
var structures = KAWSLib.BusinessLayer.Structure.GetStructuresInService();
// *** comma delimited
tw.Write("Latitude, Longitude, CountySerial, StructureType, Orientation, District, RoutePre, RouteNo, LocationDesc");
foreach (var s in structures)
{
tw.Write(Environment.NewLine + string.Format("{0:#.000000},{1:#.000000},{2},{3},{4},{5},{6},{7},{8}", s.LATITUDE, s.LONGITUDE, s.CO_SER, EscapeIfNeeded(s.SuperTypeLookup.SHORTDESC), EscapeIfNeeded(s.OrientationLookup.SHORTDESC), s.DISTRICT, s.ROUTE_PREFIX, s.RouteValue, EscapeIfNeeded(s.LOC_DESC)));
}
tw.Flush();
byte[] bytes = ms.ToArray();
ms.Close();
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=" + string.Format("kaws-structures-{0:yyyy.MM.dd}.csv", DateTime.Today));
Response.BinaryWrite(bytes);
Response.End();
}
string EscapeIfNeeded(string s)
{
if (s.Contains(","))
{
return "\"" + s.Replace("\"", "\"\"") + "\"";
}
else
{
return s;
}
}
Below will cause a problem for excel. In excel the first quote will become part of the data and consequently than separate at the embedded comma. Spaces bad.
tw.Write(Environment.NewLine + string.Format("{0:#.000000}, {1:#.000000}, {2}, {3}, {4}, {5}, {6}, {7}, {8}", s.LATITUDE, s.LONGITUDE, s.CO_SER, EscapeIfNeeded(s.SuperTypeLookup.SHORTDESC), EscapeIfNeeded(s.OrientationLookup.SHORTDESC), s.DISTRICT, s.ROUTE_PREFIX, s.RouteValue, EscapeIfNeeded(s.LOC_DESC)));
This is very simple, and the answer can be seen in this Microsoft KB Article: How to write binary files to the browser using ASP.NET and C#

Categories

Resources