Расширение клиент-серверных компонентов для fp3 файлов

13.12.2021

FastReport VCL

Для построения корпоративной системы важную роль играет документооборот. Если в вашей системе используется FastReport VCL, то основным документом готового отчета является формат fp3.

Чтобы облегчить формирование документов разных типов из внутреннего формата, не перестраивая отчеты, мы расширили наши клиент-серверные компоненты, которые теперь позволяют принимать от клиентов формат fp3 и конвертировать его в любой доступный формат экспорта.

Это сопровождается системой кэширования, где сервер может отказаться от приёма файла, который у него уже есть и работать с кэшем. Также прилагаются мелкие настройки, например, максимальный размер принимаемого файла.

Для того чтобы ваш сервер смог принимать и обрабатывать fp3 файлы, нужно добавить пару настроек в конфиг (xml файл с настройками).

Новые настройки в конфиге

HeaderMaxSize - максимальный размер HTTP заголовка в байтах. Максимальное значение 16384.

ContentMaxSize - максимальный размер загружаемых fp3 файлов в мегабайтах (0 - без ограничений).

Мы также обновили наши демонстрационные проекты, которые вы можете скачать тут.

Для клиентской части мы разберём php скрипт в роли клиента, но, конечно, клиент может быть написан на условно любом языке программирования.

HTML форма для скрипта:

<html>
 <head>
 <meta charset="utf-8">
 </head>
 <body> 
 <form enctype="multipart/form-data" action="post.php" method="POST">
 <!-- Поле MAX_FILE_SIZE должно быть указано до поля загрузки файла -->
 <input type="hidden" name="MAX_FILE_SIZE" value="3000000000" />
 <!-- Название элемента input определяет имя в массиве $_FILES -->
 Send this: <input name="userfile" type="file" />
 <input type="submit" value="Send File" />
 </form>
 </body>
</html>

Post.php:

<?php
//Адрес FastReport-сервера
$host ='http://localhost:8097';
//Формат экспорта
$exportFormat = 'PDF';
 
//Имя файла, который выбрал пользователь
$OldName = $_FILES['userfile']['name'];
//Его фактическое имя после нашей загрузки (заменяем расширение tmp на fp3)
$name = substr_replace($_FILES['userfile']['tmp_name'], 'fp3', -3);
//Сохраняем на php сервере
move_uploaded_file($_FILES['userfile']['tmp_name'], $name);
//Открываем его
$file = new \CURLFile($name);
//Инициализируем сеанс cURL
$ch = curl_init();
 
//Устанавливаем загружаемый URL
curl_setopt($ch, CURLOPT_URL, $host);
//Выбираем метод запроса (Post)
curl_setopt($ch, CURLOPT_POST, true);
//Для чтения возвращаемых данных
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//Для чтения возвращаемого заголовка
curl_setopt($ch, CURLOPT_HEADER, 1);
//При больших файлах надо увеличить ожидание отправки (по умолчанию 30 секунд),
//установлен 0, что означает бесконечное ожидание.
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
 
//Генерация MD5 для файла.
$md5 = md5_file($name);
//Отправка формата и MD5
curl_setopt($ch, CURLOPT_HTTPHEADER , array('Format-Export: '.$exportFormat, 'Content-MD5: '.$md5));
 
//Запаковка файла перед отправкой
$data = array($exportFormat => $file);
//Отправка файла
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
//Ожидание ответа
$response = curl_exec($ch);
 
//Отладочная проверка возврата
if (empty($response))
{
 echo 'received an empty response';
}
else
{
 if (strstr($response, 'HTTP/1.1 301') == '')
 {
 //Что-то пошло не так. Сервер вернул неожиданный ответ
 echo '404';
 curl_close($ch);
 exit;
 }
 if (curl_errno($ch))
 {
 //Сервер вернул ошибку, которую мы выводим красным цветом
 echo '<span style="color:red">';
 echo 'error: '.curl_error($ch);
 echo '</span>';
 }
 else
 {
 //Извлекаем из ответа FastReport-сервера адрес для получения результатов экспорта
 $Location = GetLocationFromHeader($response);
 if (empty($Location))
 {
 echo 'error: Location not found';
 }
 else
 {
 //Можно направить клиента на адрес для получения файла, но не при архитектуре, где FastReport-сервер 
 //соединен с php-сервером локально и у него нет доступа к Интернету. Придется все скачать средствами
 //php-сервера и это безопаснее с точки зрения логики, чтобы предотвратить доступ к чужим документам.
 $file = file_get_contents_curl($host.$Location);
 if (empty($file))
 {
 echo 'error: file missing';
 }
 {
 //Мы должны сгенерировать новое имя для отдачи файла клиенту.
 //Реализовано всё следующим образом: Имя файла такое-же какое было у клиента, 
 //а для получения расширения анализируется ответ сервера.
 //Извлечем формат из ответа. Берем старое имя, которое нам присылал клиент и заменяем расширение 
 //на результат экспорта (если он прислал 123.fp3, то получим 123.pdf).
 $Format = getExtension(GetFileNameFromLocation($Location));
 $OldName = substr_replace($OldName, $Format, -3);
 
 //Передача файлов от php-сервера клиенту
 header('X-Accel-Redirect: storage/'.$OldName);
 header('Content-Disposition: attachment; filename="'.$OldName.'"');
 echo $file;
 }
 }
 }
}
curl_close($ch);
 
 
 
//Вспомогательные функции
//Извлекает из ответа сервера адрес для получения результата конвертирования
function GetLocationFromHeader($arg_1)
{
 $Location = strstr($arg_1, 'Location');
 $Location = strstr($Location, '/');
 $Location = substr($Location, 0, strrpos($Location, 'SessionId')-2);
 return $Location;
}
 
//Получение имени файла из ответа
function GetFileNameFromLocation($arg_1)
{
 $FN = substr($arg_1, strripos($arg_1, '/')+1, strlen($arg_1));
 return $FN;
}
 
//Получение расширения из имени файла
function getExtension($fileName)
{
 return substr($fileName, strrpos($fileName, '.') + 1);
}
 
//Более быстрый аналог функции file_get_contents
function file_get_contents_curl($url)
{
 $ch = curl_init();
 
 curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
 curl_setopt($ch, CURLOPT_HEADER, 0);
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 curl_setopt($ch, CURLOPT_URL, $url);
 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
 
 $data = curl_exec($ch);
 curl_close($ch);
 
 return $data;
}
?>

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

26 июля 2024

Обновление HTMLObject в виде плагина для FastReport .NET

Подробная инструкция по использованию нового плагина HTMLObject, использующий разбиение DOM HTML на объекты отчета FastReport.
11 декабря 2023

Новый транспорт S3 (Amazon) в FastReport VCL

В этой статье мы рассмотрим новый транспорт в S3 (Amazon) для FastReport VCL, являющийся объектным хранилищем файлов и бакетов.
26 сентября 2023

Новый транспорт NextCloud в FastReport VCL

Подробная инструкция по использованию нового транспорта для подключения к NextCloud чтобы сохранять построенные отчёты в облако.