Одной из часто встречающихся в процессе создания приложений задач является генерация различных документов в каком-либо из популярных форматов. Есть несколько привычных путей к желаемому результату – от подключения готовой библиотеки до брутального штудирования спецификации формата с последующим написанием необходимого кода. Но, независимо от выбранного варианта, было бы неплохо проверить, что полученный документ будет хорошо восприниматься и редактироваться стандартными средствами. О некоторых способах такой проверки и пойдет речь под катом.
Оговорюсь сразу, что под проверкой в данном случае я понимаю проверку на соответствие полученного результата стандарту выбранного формата, позволяющую с достаточно большой вероятностью утверждать, что документ как минимум откроется в Word или Excel без неприятных сообщений о возникших проблемах с предложением попытаться восстановить поврежденный файл. Независимо от того, насколько серьёзным является разрабатываемое приложение и насколько солидным должен быть набор поддерживаемых типов файлов, в него скорее всего будут входить стандартные форматы Office Open XML, более известные как docx и xlsx, а также их двоичные предшественники, doc и xls. О некоторых механизмах работы с ними я и буду рассказывать.
Прогулки с динозаврами
Хотя Microsoft и выложила на своем сайте официальные спецификации форматов doc и xls, они нередко оказываются скупы и немногословны в своих описаниях. Стоит понимать, что со временем форматы претерпевали существенные изменения (с сохранением обратной совместимости). При этом единый подход использовался не только для хранения документов Word, но и для таблиц Excel и презентаций PowerPoint. Проще говоря, даже при наличии мануалов возможностей почувствовать себя Жоржем Кювье, пытающимся восстановить облик загадочной зверюшки по разрозненным костям, предостаточно. К счастью, существует способ локализовать имеющиеся в документе проблемы без необходимости расхлебывать байтовую кашу в любимом hex-редакторе.
Способ этот заключается в использовании Microsoft Office Binary File Format Validator. Это достаточно простая в использовании утилита командной строки, в комплекте с которой идут три dll-ки (по одной на каждый поддерживаемый формат – doc, xls, ppt). Несмотря на то, что с момента первого официального анонса на сайте так и осталась выложенной бета-версия, инструмент вполне себе работоспособен и справляется со своими задачами. Единственная существенная проблема, с которой я столкнулся за время работы с валидатором – это отсутствие поддержки кириллических имен файлов. Для запуска утилиты достаточно ввести команду
bffvalidator.exe [-l log.xml] filename.ext
где filename.ext — это имя исследуемого файла, а -l log.xml – необязательный параметр, указывающий куда сохранить лог (по умолчанию лог пишется в ту же папку, где лежит проверяемый документ).
Для облегчения жизни и уменьшения количества рутинных действий я использую два сценария работы с валидатором. При проверке отдельного файла удобно пользоваться Far-ом: достаточно завести отдельную папку, например, c:\Temp\Bff, положить туда экзешник валидатора и сопутствующие dll-ли, а потом завести команду через F9-Commands-File Associations:
После этого проверить подозрительный файл можно будет буквально в пару нажатий на клавиатуру. Другой сценарий, который имеет смысл — заставить приложение сгенерировать набор тестовых файлов, и затем при помощи несложного кода прогнать проверку по всему набору, например так:
public class FileFormatValidationFailedException : Exception { public FileFormatValidationFailedException(string msg) : base(msg) { } } public void RunBFFValidator(string filePath) { string fileName = Path.GetFileName(filePath); string workingDirectory = Path.GetDirectoryName(filePath); string startupPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); StageName = String.Format("RUNNING BFFValidator for FILE {0}", fileName); outputManager.BeginWriteInfoLine(String.Format("Running BFFValidator for saved file '{0}'", fileName)); ProcessStartInfo startInfo = new ProcessStartInfo(Path.Combine(startupPath, "BFFValidator.exe")); startInfo.Arguments = string.Format("-l bfflog.xml \"{0}\"", Path.Combine(workingDirectory, fileName)); startInfo.WorkingDirectory = workingDirectory; startInfo.WindowStyle = ProcessWindowStyle.Hidden; Process validatorProcess = Process.Start(startInfo); validatorProcess.WaitForExit(); if (validatorProcess.ExitCode != 0) { using (StreamReader reader = new StreamReader(Path.Combine(workingDirectory, "bfflog.xml"))) { string logContent = reader.ReadToEnd(); throw new FileFormatValidationFailedException(logContent); } } }
Вторая часть Мерлезонского балета
Ситуация с Office Open XML существенно проще. Если открыть несколько файлов hex-редактором, то в начале данных можно будет увидеть инициалы Фила Каца:
Это означает, что файлы представляют собой переименованный в docx/xlsx zip-архив, который можно открыть и увидеть вполне читаемую структуру. Однако и в этом случае можно не пытаться вручную искать расхождения с документацией из Редмонда, а поручить анализ файла специально предназначенным для этого инструментам. Для этого качаем Open XML SDK 2.5 и устанавливаем его (нам понадобятся OpenXMLSDKV25.msi и OpenXMLSDKToolV25.msi). После этого можно будет создать приложение для проверки файлов на наличие невалидной разметки (понадобится референс на DocumentFormat.OpenXml.dll). Простейший код для анализа документов выглядит следующим образом:
public void RunOpenXmlValidation(string filePath, string openXmlFormatVersion) { string fileName = Path.GetFileName(filePath); StageName = String.Format("RUNNING OpenXmlValidation for FILE {0}", fileName); outputManager.BeginWriteInfoLine(String.Format("Running OpenXmlValidation for saved file '{0}'", fileName)); using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(filePath, false)) { DocumentFormat.OpenXml.FileFormatVersions formatVersion = DocumentFormat.OpenXml.FileFormatVersions.Office2010; if (openXmlFormatVersion == "office2007") formatVersion = DocumentFormat.OpenXml.FileFormatVersions.Office2007; else if (openXmlFormatVersion == "office2013") formatVersion = DocumentFormat.OpenXml.FileFormatVersions.Office2013; OpenXmlValidator validator = new OpenXmlValidator(formatVersion); var errors = validator.Validate(wordDoc); StringBuilder builder = new StringBuilder(); foreach (ValidationErrorInfo error in errors) { string errorMsg = string.Format("{0}: {1}, {2}, {3}", error.ErrorType.ToString(), error.Part.Uri, error.Path.XPath, error.Node.LocalName); builder.AppendLine(errorMsg); builder.AppendLine(error.Description); } string logContent = builder.ToString(); if (!string.IsNullOrEmpty(logContent)) throw new FileFormatValidationFailedException(logContent); } }
And we need to go deeper…
Рассмотренные выше способы валидации стоит рассматривать как попытку быстрого поиска, где именно могут быть проблемы в документе, а не как абсолютную гарантию того, что все в порядке. Вполне реальны ситуации, когда валидатор выдает сообщение об ошибке, а Word или Excel нормально открывает файл и наоборот – не удается открыть прошедший валидацию документ. Поэтому если необходима более надежная проверка, то не обойтись без использования COM. Это требует установленного Microsoft Office, не является thread-safe, требует дополнительных телодвижений для x64, зато позволяет убедиться в соответствие документа требованиям MS-Office и поисследовать его структуру с точки зрения целевой платформы.
Внеклассное чтение
Если понадобится более серьезный анализ файла с углубленным пониманием его структуры, то можно обратиться к документации по OpenXML SDK и непосредственно форматам.
Также при исследовании внутреннего устройства документов может помочь утилита OffVis.
Несколько полезных ссылок по взаимодействию с офисными приложениями: Primary Interop Assemblies (PIAs), Microsoft.Office.Interop.Excel namespace, Microsoft.Office.Interop.Word namespace.
Надеюсь, что использование полезных инструментов от Microsoft сбережет ваше время и нервы. Спасибо за внимание!
ссылка на оригинал статьи http://habrahabr.ru/post/270205/
Добавить комментарий