
Экспорт таблицы в Excel из ASP.NET: Content-Disposition и GridView
Пользователи рано или поздно просят кнопку «Выгрузить в Excel». Разбираем самый простой способ отдать таблицу из ASP.NET, не ставя на сервер ни Office, ни сторонних библиотек.
Идея: браузеру говорят, что это файл
Браузер решает, показать ответ сервера или предложить его сохранить, по двум HTTP-заголовкам. Content-Type сообщает, что за данные пришли, а Content-Disposition со значением attachment просит не открывать содержимое в окне, а скачать его под указанным именем. Если указать тип application/vnd.ms-excel и имя с расширением .xls, браузер откроет загрузку, а Windows предложит открыть файл в Excel.
Сам Excel умеет читать не только свой двоичный формат, но и HTML-таблицы. Именно на этом построен приём: страница выводит обычную разметку table, а Excel при открытии превращает её в лист с ячейками. Никаких библиотек и установленного Office на сервере не требуется, поэтому способ годится даже для дешёвого хостинга.
Заголовки ответа
Перед выводом данных ответ нужно очистить, иначе в файл попадёт разметка самой страницы. Затем задаются тип содержимого, имя файла и кодировка. Для русских данных надёжнее всего UTF-8 с BOM: без метки старые версии Excel решат, что текст в кодировке Windows и покажут кракозябры.
Имя файла с кириллицей в заголовке Content-Disposition разные браузеры понимали по-разному, поэтому для совместимости имя лучше делать латиницей, например report_2008-06-23.xls.
Response.Clear()
Response.Buffer = True
Response.AddHeader("Content-Disposition", "attachment; filename=report.xls")
Response.ContentType = "application/vnd.ms-excel"
Response.ContentEncoding = System.Text.Encoding.UTF8
Response.BinaryWrite(System.Text.Encoding.UTF8.GetPreamble()) ' BOM для ExcelВыгрузка через GridView
Удобнее всего отдавать табличные данные элементом GridView: он уже умеет строить HTML-таблицу из DataTable или результата запроса. Элемент не обязательно размещать на странице. Его можно создать в коде, привязать к данным и попросить отрисовать себя в строку через HtmlTextWriter.
Такой подход позволяет повесить экспорт на обычную кнопку рядом с видимой таблицей. В примере данные для простоты собираются в DataTable в коде, в реальном проекте здесь будет запрос к базе.
Imports System.Data
Imports System.IO
Imports System.Web.UI
Imports System.Web.UI.WebControls
Partial Class Export
Inherits System.Web.UI.Page
Private Function GetData() As DataTable
Dim dt As New DataTable()
dt.Columns.Add("Номер", GetType(Integer))
dt.Columns.Add("Описание", GetType(String))
For i As Integer = 1 To 100
dt.Rows.Add(i, "Строка " & i)
Next
Return dt
End Function
Protected Sub btnExport_Click(sender As Object, e As EventArgs) Handles btnExport.Click
Dim grid As New GridView()
grid.DataSource = GetData()
grid.DataBind()
Response.Clear()
Response.AddHeader("Content-Disposition", "attachment; filename=report.xls")
Response.ContentType = "application/vnd.ms-excel"
Response.ContentEncoding = System.Text.Encoding.UTF8
Response.BinaryWrite(System.Text.Encoding.UTF8.GetPreamble())
Using sw As New StringWriter(), hw As New HtmlTextWriter(sw)
grid.RenderControl(hw) ' таблица в HTML
Response.Write(sw.ToString())
End Using
Response.End()
End Sub
End ClassОформление и форматы ячеек
Раз Excel читает HTML, он понимает и часть CSS. Цвет фона заголовка, жирный шрифт и рамки, заданные стилями в ячейках, перенесутся в лист. Есть и специальное свойство mso-number-format, которое управляет форматом ячейки так же, как диалог «Формат ячеек».
Самая частая жалоба после первого запуска: пропали ведущие нули. Excel видит в ячейке 00123 число и превращает его в 123. То же происходит с длинными номерами счетов (они уходят в экспоненту) и с датами, которые Excel пытается распознать по своей локали. Решение: явно пометить такие столбцы текстовым форматом.
/* Вставляется в <style> перед таблицей */
.text { mso-number-format: "\@"; } /* текст, ноли сохраняются */
.money { mso-number-format: "#,##0.00"; } /* деньги с копейками */
.date { mso-number-format: "dd\.mm\.yyyy"; } /* дата */
th { background: #dce6f1; font-weight: bold; }Ограничения подхода
Нужно честно понимать, что получается на выходе. Это не настоящий файл Excel, а HTML с расширением .xls. Начиная с Excel 2007 при открытии появляется предупреждение о несоответствии формата и расширения. Пользователь может нажать «Да», и всё откроется, но выглядит это настораживающе, а политика безопасности в компании может такие файлы вообще блокировать.
Кроме того, в таком файле нет нескольких листов, формул в привычном виде и надёжной типизации. Для внутренних отчётов приём по-прежнему годится, а для документов, которые уходят клиентам, лучше формировать настоящий xlsx.
- Предупреждение Excel о формате при открытии.
- Один лист, без формул и диаграмм.
- Большие выгрузки занимают много памяти, так как HTML собирается целиком.
- Типы ячеек угадываются Excel, если не задать mso-number-format.
Альтернатива: CSV
Если оформление не нужно, проще отдать CSV: значения через точку с запятой (её ожидает Excel с русской локалью), строки через перевод строки, ContentType text/csv и BOM в начале. Файл получится компактнее HTML, его без проблем прочитают и другие программы. Не забудьте экранировать значения, содержащие разделитель или кавычки: их заключают в двойные кавычки, а внутренние кавычки удваивают.
Как это делают сегодня
Вопросы и ответы
Почему Excel ругается, что формат файла не совпадает с расширением?
Файл на самом деле HTML, а расширение .xls обещает двоичный формат. Это ограничение подхода, убрать предупреждение можно только генерацией настоящего xlsx.
Как сохранить ведущие нули в номерах?
Добавьте ячейкам стиль mso-number-format:"\@". Excel воспримет их как текст и не станет преобразовывать в число.
Можно ли поставить Excel на сервер и генерировать файлы через Interop?
Microsoft прямо не рекомендует автоматизацию Office на сервере: процессы зависают, плохо масштабируются и требуют лицензий. Используйте библиотеки для Open XML.