Часто надо бывает из приложения на ASP.NET сгенерировать отчёт на сервере в OpenXML-формате.
Есть несколько привычных способов сделать это:
OfficeOpenXML – это то, в чём вы по умолчанию сохраняете документы, работая в Word и Excel: docx и xlsx. Файл представляет собой zip-архив. Его можно переименовать в zip, открыть архиватором и рассмотреть, что внутри:

Отчёты в OOXML хорошо воспринимаются и редактируются привычными средствами. Я бы не рекомендовал в серьёзных приложениях ограничиваться именно этим форматом, но советую поддерживать его.
Нам понадобятся:

Запускаем Open XML SDK 2.0 Productivity Tool:

Эта тулза очень простая и умеет делать две маленькие, но важные операции:
Загружаем в программулину наш документ и кликаем «Reflect Code»:

Слева мы видим структуру документа – те же файлы, что присутствуют в архиве, и представление их содержимого.
Ноды в дереве можно выделять: справа видно содержимое ноды в виде XML и код, который может сгенерировать именно этот кусочек. На моём примере виден один абзац из тела документа. Оно как раз живёт в word/document.xml.
Если выделить корень дерева (сам документ) – получим код для всего документа.
Что же внутри сгенерированного класса?
Во-первых, там один единственный открытый метод:
Вот тут вставляется текст, который будет в документе:
Как видно из названий private-методов в коде, OpenXml-документ состоит из частей (part). Для генерации каждой части сделан отдельный метод.
Наиболее любознательные, конечно же, ехидно улыбнувшись, вставили в документ картинку.
Картинки хранятся прямо в этом файле, в виде base64, вот тут:
Рефакторинг картинок и замена статического контента на динамический оставим читателю в качестве упражнения.
А вот метод, который генерирует не файл, а массив байтов – для отдачи клиенту из asp.net без временных файлов:
Всё, код для генерации отчёта в формате docx готов.
Осталось заменить контент на динамический. Мы же не делали всё это ради того, чтобы всё время отдавать одно и то же, ведь правда? И добавить на страничку ссылку «Скачать в формате Word».
Итак, мы сгенерировали код по документу. Добавили туда много данных, зарефакторили его, внедрили в production. И вот нам надо поменять шрифт и текст в отчёте. Как же это сделать? Кода много, искать в нём долго.
Оказывается, всё очень просто, нам поможет фича сравнения документов:
Кстати, эту фичу ещё очень удобно использовать, если вы только знакомитесь с форматом OpenXML: добавляете что-то в документ и смотрите, что изменилось. Поможет тем, кто выбрал способ «Ъ», о котором говорилось в начале статьи.
Я считаю, что использование DocumentFormat.OpenXml для генерации отчётов в web-приложениях – правильный выбор. Полезная тулза из SDK позволит вам не тратить время зря.
Про OpenXML SDK: msdn.microsoft.com/en-us/library/bb448854(office.14).aspx
Про OpenXML (если кто с ним не знаком): en.wikipedia.org/wiki/Office_Open_XML
Удачи! Спасибо за внимание.
Есть несколько привычных способов сделать это:
- «Нашёл, слинковал, заюзал» – идём в Гугл, ищем библиотеку для генерации docx или xlsx, подключаем, разбираемся, генерируем. Это привычно, но долго.
- «Фу» – использовать COM. Это не рекомендуется, требует установленного Microsoft Office на сервере, не очень thread-safe, с x64 не дружит и вообще старомодно.
- «Ъ» – разобраться с форматом, собрать из XML и зазипать. Брутально.
- «Microsoft way» – об этом способе рассказывается под катом.
Небольшое введение
OfficeOpenXML – это то, в чём вы по умолчанию сохраняете документы, работая в Word и Excel: docx и xlsx. Файл представляет собой zip-архив. Его можно переименовать в zip, открыть архиватором и рассмотреть, что внутри:

Отчёты в OOXML хорошо воспринимаются и редактируются привычными средствами. Я бы не рекомендовал в серьёзных приложениях ограничиваться именно этим форматом, но советую поддерживать его.
Подготовка
Нам понадобятся:
- Microsoft OpenXML SDK: www.microsoft.com/downloads/en/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&displaylang=en (качать то, что больше)
- Microsoft Word
- Простейшее C#-приложение в Visual Studio

Поехали
Запускаем Open XML SDK 2.0 Productivity Tool:

Эта тулза очень простая и умеет делать две маленькие, но важные операции:
- Сгенерировать код по документу
- Сравнивать документы на уровне XML
Генерация кода
Загружаем в программулину наш документ и кликаем «Reflect Code»:

Слева мы видим структуру документа – те же файлы, что присутствуют в архиве, и представление их содержимого.
Ноды в дереве можно выделять: справа видно содержимое ноды в виде XML и код, который может сгенерировать именно этот кусочек. На моём примере виден один абзац из тела документа. Оно как раз живёт в word/document.xml.
Если выделить корень дерева (сам документ) – получим код для всего документа.
Теперь давайте поиспользуем этот код
- Делаем проект в Visual Studio. Пусть это будет простое консольное C#-приложение
- Добавляем референс на сборку DocumentFormat.OpenXml:
У меня она в GAC. Если вы не хотите её туда класть, можно добавить ссылку на сам файл. Отдельно скачать его можно там же, где был OpenXMLSDKTool, но по ссылке OpenXMLSDKv2.msi - Добавляем референс на WindowsBase
- Добавляем файл «GeneratedClass.cs»
- Копируем туда код из тулзы, из окошка ReflectedCode
- Закрываем файл, сохранив его, переходим в Program.cs
- Пишем метод Main:
new GeneratedCode.GeneratedClass().CreatePackage(@"D:\Temp\Output.docx");
- Запускаем
Что внутри?
Что же внутри сгенерированного класса?
Во-первых, там один единственный открытый метод:
public void CreatePackage(string filePath) {
using (WordprocessingDocument package = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document)) {
CreateParts(package);
}
}
Вот тут вставляется текст, который будет в документе:
private void GenerateMainDocumentPart1Content(MainDocumentPart mainDocumentPart1) {
Run run2 = new Run() { RsidRunProperties = "00184031" };
Text text2 = new Text();
text2.Text = "Исчисление предикатов, по определению, философски выводит структурализм, изменяя привычную реальность."; // о.О какую траву курил Яндекс?
}
Как видно из названий private-методов в коде, OpenXml-документ состоит из частей (part). Для генерации каждой части сделан отдельный метод.
Наиболее любознательные, конечно же, ехидно улыбнувшись, вставили в документ картинку.
Картинки хранятся прямо в этом файле, в виде base64, вот тут:
#region Binary Data
//...
#endregion
Завязываем бантики
Рефакторинг картинок и замена статического контента на динамический оставим читателю в качестве упражнения.
А вот метод, который генерирует не файл, а массив байтов – для отдачи клиенту из asp.net без временных файлов:
public byte[] CreatePackageAsBytes() {
using (var mstm = new MemoryStream()) {
using (WordprocessingDocument package = WordprocessingDocument.Create(mstm, WordprocessingDocumentType.Document)) {
CreateParts(package);
}
mstm.Flush();
mstm.Close();
return mstm.ToArray();
}
}
Всё, код для генерации отчёта в формате docx готов.
Осталось заменить контент на динамический. Мы же не делали всё это ради того, чтобы всё время отдавать одно и то же, ведь правда? И добавить на страничку ссылку «Скачать в формате Word».
Сравнение документов
Итак, мы сгенерировали код по документу. Добавили туда много данных, зарефакторили его, внедрили в production. И вот нам надо поменять шрифт и текст в отчёте. Как же это сделать? Кода много, искать в нём долго.
Оказывается, всё очень просто, нам поможет фича сравнения документов:
- Положим рядом старый и новый документы
- Открываем Open XML Productivity Tool, выбираем «Compare files...»:
- Открываем файлы и жмём OK. Перед нами результат сравнения:
На строчки с именами файлов можно тыкнуть и увидеть, в чём именно отличия:
В MoreOprions выбирается, что игнорировать при сравнении.
View Part Code показывает код той части, XML которой вы видите.
Уж сопоставить XML и код труда не составит.
Кстати, эту фичу ещё очень удобно использовать, если вы только знакомитесь с форматом OpenXML: добавляете что-то в документ и смотрите, что изменилось. Поможет тем, кто выбрал способ «Ъ», о котором говорилось в начале статьи.
Факты
- С Xlsx катит. Точно так же, как с docx
- Если внутри Docx график или диаграмма – всё будет хорошо
- Это всего лишь strongly-typed обёртка над библиотекой System.IO.Packaging
- На сервере не нужно ничего, кроме этой библиотеки
- Никаких проблем с x64
- Производительность на высоте
Выводы
Я считаю, что использование DocumentFormat.OpenXml для генерации отчётов в web-приложениях – правильный выбор. Полезная тулза из SDK позволит вам не тратить время зря.
Что почитать
Про OpenXML SDK: msdn.microsoft.com/en-us/library/bb448854(office.14).aspx
Про OpenXML (если кто с ним не знаком): en.wikipedia.org/wiki/Office_Open_XML
Удачи! Спасибо за внимание.