Trouble Understanding Code to Export Excel Spreadsheet from HTML - asp.net

I have been asked to make changes to an ASP.NET WebForms application written in VB (I normally use C#).
One task is to try and fix an Excel download. The client reported that he gets an error about the spreadsheet being corrupt when he attempts to open it in Excel.
The code that exports the Excel download appears in the Load event of a dedicated ASPX page. And looks something like this:
Dim mytable As New HtmlTable
mytable = [Populate HTML Table Here]
mytable = returnclass.displaytable
mytable.Border = 1
mytable.BorderColor = "#CCCCCC"
HttpContext.Current.Response.Clear()
HttpContext.Current.Response.Buffer = True
Response.Write("<html xmlns:x=""urn:schemas-microsoft-com:office:excel"">")
Response.Write("<head>")
Response.Write("<!--[if gte mso 9]><xml>")
Response.Write("<x:ExcelWorkbook>")
Response.Write("<x:ExcelWorksheets>")
Response.Write("<x:ExcelWorksheet>")
Response.Write("<x:Name>" & worksheetTitle & "</x:Name>")
Response.Write("<x:WorksheetOptions>")
Response.Write("<x:Print>")
Response.Write("<x:ValidPrinterInfo/>")
Response.Write("</x:Print>")
Response.Write("</x:WorksheetOptions>")
Response.Write("</x:ExcelWorksheet>")
Response.Write("</x:ExcelWorksheets>")
Response.Write("</x:ExcelWorkbook>")
Response.Write("</xml>")
Response.Write("<![endif]--> ")
Response.Write("</head>")
Response.Write("<body>")
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel"
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=hhexport.xls ")
HttpContext.Current.Response.Charset = ""
'ouput table to html so excel can interperet.
Me.EnableViewState = False
Dim stringWriter As New System.IO.StringWriter()
Dim htmlWriter As New System.Web.UI.HtmlTextWriter(stringWriter)
mytable.RenderControl(htmlWriter)
HttpContext.Current.Response.Write(stringWriter.ToString)
I really don't understand what this is trying to do.
Questions:
The code produces a regular ASP.NET HtmlTable and assigns it to mytable. On what planet can Excel open HTML?
I'm really kind of loss by the XML in general here, and by the <!--[if gte mso 9] comment. Can anyone help me understand what is going on here.
The result appears valid but I'm just not familiar with what the intent is here. Any tips appreciated.
EDIT
On further testing, the problem seems related to the extension given to the file (xls). The current version of Excel will go ahead and load the file if I indicate that. But all formatting is lost. Any suggestions on what type of file this would be?
EDIT
And it looks like the original author got the idea from here, although that page doesn't really describe what is happening.
UPDATE
Thanks for everyone's response. I will credit those replies according to how they addressed the questions above. However, for my purposes the code appears to have worked all along. It just appears that newer versions of Excel now warn the user that an XLS file that contains HTML is a file of a different type than suggested by the file extension. And it appears there is nothing that can be done about this except for exporting using CSV, OpenXML or some other approach. I found more details in this blog.

The code basically wraps an HTML table in some special XML tags that relate to Excel (defining a Workbook and Worksheets, etc). This is supposed to allow the output to be opened by either Excel or a browser.
To answer your questions:
The code produces a regular ASP.NET HtmlTable and assigns it to mytable. On what planet can Excel open HTML? Actually, that's a feature of Excel. You can use a special combination of XML and HTML tags to create files that are open-able on the web and in Excel. See this MSDN article: How to format an Excel workbook while streaming MIME content
I'm really kind of loss by the XML in general here, and by the <!--[if gte mso 9] comment. Can anyone help me understand what is going on here. That specific comment is checking for the availability of MS Excel (whether it's being opened by Excel or a browser), I believe. The XML is specific tags that have special meaning in MS Excel. There's a reference you can download here: Microsoft® Office HTML and XML Reference
I found this article on C# Corner to be pretty helpful in understanding this type of code: Creating a Dynamic Excel Using HTML.

As far as I know Excel has been able to read HTML for quite a while. This particular approach is pretty common, but it's definitely not best practice.
The important part of this logic is here:
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel"
HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=hhexport.xls ")
HttpContext.Current.Response.Charset = ""
'ouput table to html so excel can interperet.
Me.EnableViewState = False
Dim stringWriter As New System.IO.StringWriter()
Dim htmlWriter As New System.Web.UI.HtmlTextWriter(stringWriter)
mytable.RenderControl(htmlWriter)
HttpContext.Current.Response.Write(stringWriter.ToString)
The Response.Write logic is just being used to control the workbook and worksheet that gets outputted. If that logic was not there, the file would open with three worksheets similar to a new Excel workbook.

Related

Generating Excel Documents with ASP.NET Website

I have an ASP.NET application that helps the user create a Gridview with certain data in it. Once this table is generated I want the user to push a button and be able to save the table as an Excel document.There are two different methods I know of:
Using HtmlTextWriter with ContentType "application/vnd.ms-excel" to send the file as an HttpResponse. I use GridView1.RenderControl(htmlTextWriter) to render the gridview. This almost works, but the excel file always shows a warning when the file opens because the content doesn't match the extension. I have tried various content types to no avail. This makes sense I guess, because I'm using an HtmlWriter. It also doesn't seem a good practice.
The second thing I've tried is generating the Excel file using Office Automation. But for the file to be generated, I need to save it to disk and then read it again. From what I have read, this is the only way, because the Excel object only becomes a real Excel file once you save it. I found that the .saveas method from the Excel class would throw an exception because of write permissions, even if I tried to save in the App_Data folder. So I did some research and found that apparently Office Automation is discouraged for web services: https://support.microsoft.com/en-us/kb/257757
Microsoft does not currently recommend, and does not support,
Automation of Microsoft Office applications from any unattended,
non-interactive client application or component (including ASP,
ASP.NET, DCOM, and NT Services), because Office may exhibit unstable
behavior and/or deadlock when Office is run in this environment.
There surely must be a save way to have a website generate an Excel file and offer it to the user!? I can't imagine that this problem is unsolved or so rare that nobody cares about it, but yet I can't find any good solution to this.
the easiest (and best) way to create an excel file is by using epplus
Epplus sample for webapplication
using (ExcelPackage pck = new ExcelPackage())
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Demo");
//Load the datatable into the sheet, starting from cell A1. Print the column names on row 1
ws.Cells["A1"].LoadFromDataTable(tbl, true);
//Format the header for column 1-3
using (ExcelRange rng = ws.Cells["A1:C1"])
{
rng.Style.Font.Bold = true;
rng.Style.Fill.PatternType = ExcelFillStyle.Solid; //Set Pattern for the background to Solid
rng.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(79, 129, 189)); //Set color to dark blue
rng.Style.Font.Color.SetColor(Color.White);
}
//Example how to Format Column 1 as numeric
using (ExcelRange col = ws.Cells[2, 1, 2 + tbl.Rows.Count, 1])
{
col.Style.Numberformat.Format = "#,##0.00";
col.Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
}
//Write it back to the client
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=ExcelDemo.xlsx");
Response.BinaryWrite(pck.GetAsByteArray());
}

Opening a web page as an Excel file, but with .xlsx files

Okay, this might be a bit unusual. If there are better ways to do this (that are just as easy, I'm open to ideas). I found a while ago that I could open a web page consisting of a Gridview or a table, with titles, etc. as an excel file and it worked great! It formatted the Excel file with colors and alignment similar to the html from the page. With later versions of excel, though, it gives me a warning that the format time isn't valid before opening it, though it still seemed to work. So I tried changing the content type to a more current version of excel, but then I don't get anything at all. Here's what I have been doing (below).
Does anyone know how to change it so that I can open the page in a current version of Excel without getting the warning?
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Response.AppendHeader("Content-disposition", "attachment; filename=Filename.xls")
Response.ContentType = "application/vnd.ms-excel"
End Sub
You are not creating an Excel file.
You are creating a HTML file with a .xls file extension. That's the wrong extension for a HTML file, and that's why Excel gives you a warning. The correct extension would be .html or .htm. Unfortunately, .html files don't automatically open in Excel, so changing the extension would require your users to manually open the file in Excel instead of just double-clicking it.
I'm afraid there's no easy way to solve this. We had the same problem, and we solved it by creating a real Excel file. There are lots of Excel libraries for .NET available. We used SpreadsheetLight, because it easily allows you to copy a DataTable to an Excel file and send that file to the web client.

asp.net - exporting table

I have a big problem with exporting my table to Excel file format.
Firstly I created code which runs on server and allows me to export data to Excel. Due to the fact that my table is created dynamically from the database there is nothing WITHIN the table at that stage, so no data were exported.
My second approach was targeting the final compiled table on the client side using either javascript or a very nice jQuery plugin called "DataTables" (www.datatables.net). Both of the attempts failed. Javascript seems to be to complex for me, plus it has difficulties running in Firefox, plugin on the other hand requires a very specific table structure which I am afraid I cannot provide.
So, a new idea of mine is: grab the page just after compiling and building it on the server, but before sending it to the browser. Target THE table and source its data using function on server. Finally export data to Excel, and send the page to the browser. Now. Is it possible? And if yes, then how?
I am beginner in programming world so any constructive suggestions and criticism would be highly appreciated. I would not mind any hard code examples ;)
You can try doing something like this:
protected void btnExport_Click(object sender, EventArgs e)
{
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/vnd.ms-excel";
Response.Charset = "";
System.IO.StringWriter oStringWriter = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter oHtmlTextWriter = new System.Web.UI.HtmlTextWriter(oStringWriter);
//if you're exporting a table put the table in a placeholder and render
//the placeholder to the text writer here
grdJobs.RenderControl(oHtmlTextWriter);
Response.Write(oStringWriter.ToString());
Response.End();
}
What you need to do is export your query results in a .CSV file. CSV files can be opened in Excel no problem at all. http://wiki.asp.net/page.aspx/401/export-to-csv-file/ This shows you how to export into a .CSV format.
You're going to get a lot of suggestions instead of answers on this here. My recommendation would be to try the jQuery plugin: table2csv in order to create a more universal file format. But there are ways to target an actual Excel format, like this project.
If you want to export to actual XLS or XLSX instead of just CSV or something that just "opens" in Excel, there are third party tools that can help you with this. One example here:
http://www.officewriter.com

Downloaded word file displaying incorrectly

I am working on a website at the moment which is displaying a strange bug with generated word documents. The site has a feature on it which allows the user to download a word document containing information related to their visit. This file is generated via some vb.net code and takes an xml template of the final document and inserts the relevant content required.
The strange behaviour is that on some machines the .doc file generated displays fine and on others it displays as XML when opened in Word. Both behaviours have been seen in the same version of Office (2003) but on seperate machines. My question is really whether the error lies with the set up of word on the individual machines, or whether there is an error in the code.
The code to create the file and download it is as follows:
Response.Clear()
Response.ClearHeaders()
Response.AddHeader("content-disposition", "inline; filename=MyNewFile")
Response.ContentType = "application/msword"
'Create the word file as a byte array based off an xml template document'
Dim objWordGenerator As New WordFileGenerator
Response.BinaryWrite(objWordGenerator.GetWordBytes)
Response.Flush()
Response.Clear()
Response.End()
The actual xml template is quite large so probably not suitable to post here but I can provide any more information if necessary.
Update:
Having managed to fix the original bug (it turns out that the original filename being used didn't have the .doc extension) I have found another bit of strange behaviour.
When the file is opened it opens in Word correctly, however when you go to save it the default file type is XML. When saved as an XML file it will open in Word correctly, but I feel this is slightly confusing behaviour for the end user. I would like the file to default to saving as a DOC file instead. Is there a way to force this to happen?
Update 2:
Below is a section of the XML that relates to the Document properties. The rest of the document deals with content and styles etc, so my assumption is that this is the most relevant section. To reiterate, my problem is that when the downloaded .doc file is opened in word, the default "save as" option is as an XML file.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core" xmlns:aml="http://schemas.microsoft.com/aml/2001/core" xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no" xml:space="preserve">
<o:DocumentProperties>
<o:Title>Fancy Word Doc</o:Title>
<o:Author>Bob Bobertson</o:Author>
<o:Characters>999</o:Characters>
<o:Company>A Fancy Company</o:Company>
<o:Version>1.1.1</o:Version>
</o:DocumentProperties>
Cheers
The File -> SaveAs filetype is XML because that is what the file open in Word is. If you want it to say 'Word Document (*.doc) then you will need to create a real Word document on the server and not an XML. Just by putting a .doc extension on the filename doesn't change it's real contents. Word knows the file type that is loaded into it and suggests that as the file type when saving. I don't know of any way to override this behavior.
I've been using Office XML with Excel for awhile now and this is very similar to the code that I'm using to send it down to the client. You might want to try and see if it works for you.
Dim xml As XmlDocument = New XmlDocument()
xml.Load("report.doc")
Response.ContentType = "application/vnd.ms-word"
Response.AppendHeader("CONTENT-DISPOSITION", "attachment; filename=report.doc")
Response.Write(xml.OuterXml)
Try it with firefox and you will probably find that it will be saved with the correct extension.
IIRC, since version 3 IE prefers to ignore the mime type and sniff the file content to see what the "correct" file format is. Maybe is uses the magic cookie?
Is this Word 2007 or later? Try
Response.AddHeader("content-disposition", "attachment; filename='MyNewFile.doc'")
attachment encourages the browser to save the file instead of displaying it.
I ran some tests and could not reproduce your problem on my system in Word 2003. Without a specific example (and actual file that is misbehaving), it would be pure speculation to make any suggestions.

How can I export my ASP.NET page to Excel?

How can I export the data in my webapp to an Excel sheet from ASP.NET (VB.NET,SQL 2005)?
change the contenttype of your ASP.Net page
Response.ContentType = "application/ms-excel"
One of my most popular blogs is how to generate an Excel document from .NET code, using the Excel XML markup (this is not OpenXML, it's standard Excel XML) - http://www.aaron-powell.com/linq-to-xml-to-excel
I also link off to an easier way to do it with VB 9.
Although this is .NET 3.5 code it could easily be done in .NET 2.0 using XmlDocument and creating the nodes that way.
Then it's just a matter to set the right response headers and streaming back in the response.
SpreadsheetGear for .NET will do it. You can find a bunch of live ASP.NET samples with C# & VB.NET source on this page.
Disclaimer: I own SpreadsheetGear LLC
If you can display your data in a GridView control, it inherently supports "right-click-->Export to Excel" without having to write any code whatsoever.
SQL Server Reporting services would be the best way to export data from an application into Excel.
If you dont have access to / dont wan't to use reporting services depending on the data you want to extract / format possibly using a CSV structure instead of Excel may be easiest.
Use the Microsoft.Office.Interop.Excel dlls to create excel files with your data and then provide links to download the files using Hunter Daley's download method...
As a general solution, you may want to consider writing handler (ashx) for exporting -- and pass either the query parameters to recreate the query to generate the data or an identifier to get the data from the cache (if cached). Depending on whether CSV is sufficient for your Excel export you could just format the data and send it back, setting the ContentType as #Hunter suggests or use the primary interop assemblies (which would require Excel on the server) to construct a real Excel spreadsheet and serialize it to the response stream.
I prefer to use a OLEDB connection string.
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Excel.xls;Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";
Not sure about exporting a page but if you just want to export a dataset or datatable
HttpContext.Current.Response.Clear()
HttpContext.Current.Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", fileName))
HttpContext.Current.Response.ContentType = "application/ms-excel"
Dim sw As StringWriter = New StringWriter
Dim htw As HtmlTextWriter = New HtmlTextWriter(sw)
Dim table As Table = New Table
table.RenderControl(htw)
' render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString)
HttpContext.Current.Response.End()
I use almost the same exact code as CodeKiwi. I would use that if you have a DataTable and want to stream it to the client browser.
If you want a file, you could also do a simple loop through each row/column, create a CSV file and I guess provide a link to the client - you can use a file extension of CSV or XLS. Or if you stream the resulting file to the client it will prompt them if they want to open or save it to disk.
The interops are (well were last time I tried them) great for small datasets, but didn't scale well - horrifically slow for larger datasets.

Resources