Перейти к содержанию

Конвертация файлов в PDF

Конвертация файлов в PDF

Рассмотрим задачу по конвертации файлов офисных форматов (doc, docx, xls, xlsx и др.) в файлы PDF.

Конвертация файла выполняется посредством создания и отслеживания отдельных процессов unoconv и LibreOffice. Хостинг процессов и управление их жизненным циклом выполняется посредством плагина Chronos FileConverterPlugin. Т.о. для конвертации на сервере приложений должен быть установлен и настроен LibreOffice (в руководстве по установке для web-клиента), а также должен работать сервис Chronos.

Из веб-сервиса можно вызвать API, которое отдаст операцию в Chronos и опционально дождётся её завершения. При конвертации по умолчанию используются кэш файлов, и если запрошенный файл уже есть в кэше, то вызов API конвертации будет на 100% синхронный (мгновенный), в этом сценарии Chronos не задействуется.

Синхронная конвертация

В любом серверном расширении, в котором требуется инициировать конвертацию в PDF, резолвится Tessa.FileConverters.IFileConverter из Unity.

  1. Есть метод-расширение для создания объекта-запроса на конвертацию (не создаёт операций): fileConverter.GetRequestOrThrow(FileConverterEventNames.Unknown, FileConverterFormat.Pdf, versionRowID)

  2. В полученном запросе можно установить флаги, их описание во всплывающей подсказке: request.Flags = ...

  3. Есть метод ConvertFile, который получает запрос на конвертацию (созданный предыдущим методом), и возвращает результат сразу же, как только Chronos “подхватит” операцию, выполнит конвертацию и вернёт результат.

  4. Есть метод ConvertFileAsync, который делает то же самое, но в отдельном потоке, и, не дожидаясь завершения, возвращает Task, в котором уже всё ожидание происходит. К возвращённому Task-у можно добавить await / ContinueWith для асинхронного действия с продолжением, или же вызвать метод task.Wait(timeoutMilliseconds) для синхронного ожидания с таймаутом.

  5. На возвращённом response-е вызывается метод using(Stream content = response.GetStreamOrThrow()) { }.

// требуется только идентификатор версии файла Guid versionRowID = ...;

// обычно запрос из Unity выполняют через конструктор var fileConverter = unityContainer.Resolve<IFileConverter>();

var request = fileConverter.GetRequestOrThrow("MyCustomConversionToPdf", FileConverterFormat.Pdf, versionRowID); var response = await fileConverter.ConvertFileAsync(request);

using (Stream stream = response.GetStreamOrThrow()) { // stream содержит сконвертированный в PDF файл, копируем его в карточку, выгружаем и др. }

Асинхронная конвертация

Есть также механизмы для полностью асинхронной конвертации, при этом не задействует кэш файлов.

  1. Для этого в запросе на конвертацию в качестве eventName укажите любую строковую константу "АлиасВашегоСобытия".

  2. Для отключения кэша установите флаги в запросе: IgnoreCacheBeforeConversion, DoNotCacheResult, DoNotAwaitResult, WithoutResponse.

  3. Напишите расширение-наследник FileConverterExtension, которое будет вызвано из плагина Chronos при завершении конвертации. Регистрация расширения похожа на обычную регистрацию, используется интерфейс IFileConverterExtension, методы .WithOrder(AfterPlatform) и .WhenFileConverterEventNames("АлиасВашегоСобытия").

В этом случае файл будет сконвертирован, помещён во временную папку на сервере, на котором установлен Chronos. Сразу после успешной конвертации операция удаляется из “активных операций”. Далее будет вызвано расширение IFileConverterExtension на указанное событие. Расширение получает файл из своего контекста. По завершении файл удаляется из временной папки. Т.е. это сценарий, когда надо сконвертировать, выполнить постобработку и “забыть” об операции до момента её завершения, при этом конвертация может выполняться неопределённо длительное время.

Большой плюс этого подхода - отсутствует поток, ожидающий окончания конвертации и постоянно выполняющий проверки статуса операции. Этот подход несколько сложнее синхронного подхода, поскольку требует дополнительного расширения. Также не используется кэш, т.е. повторная конвертация того же файла будет выполняться заново.

Расширение, выполняемое в веб-сервисе (например, CardStoreExtension), выглядит следующим образом:

// требуется только идентификатор версии файла Guid versionRowID = ...;

// обычно запрос из Unity выполняют через конструктор var fileConverter = unityContainer.Resolve<IFileConverter>();

var request = fileConverter.GetRequestOrThrow("MyCustomConversionToPdf", FileConverterFormat.Pdf, versionRowID); request.Flags |= FileConverterRequestFlags.IgnoreCacheBeforeConversion | FileConverterRequestFlags.DoNotCacheResult | FileConverterRequestFlags.DoNotAwaitResult | FileConverterRequestFlags.WithoutResponse;

// любую дополнительную (сериализуемую) информацию можно передать через Info request.Info["CardID"] = Guid.NewGuid();

fileConverter.ConvertFile(request);

Расширение для обработки результатов конвертации располагается в сборке Tessa.Extensions.Server, но фактически оно будет выполнено не для веб-сервиса, а в плагине Chronos, когда конвертация будет завершена. Поэтому не забудьте обновить файл со сборкой Tessa.Extensions.Server.dll в папке расширений в Chronos.

public sealed class MyFileConverterExtension : FileConverterExtension { public override void AfterRequest(IFileConverterContext context) { if (!context.RequestIsSuccessful) { return; }

// переданная дополнительная информация доступна из контекста Guid? cardID = context.Request.Info.TryGet<Guid?>("CardID");

using (Stream stream = context.GetOutputStreamOrThrow()) { // stream содержит сконвертированный в PDF файл, копируем его в карточку, выгружаем и др. } } }

Регистрация расширения в классе Registrator:

extensionContainer .RegisterExtension<IFileConverterExtension, MyFileConverterExtension>(x => x .WithOrder(ExtensionStage.AfterPlatform, 1) .WhenFileConverterEventNames("MyCustomConversionToPdf")) ;

Back to top