Для построения корпоративной системы важную роль играет документооборот. Если в вашей системе используется 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; } ?>
Таким образом вы сможете избавиться от нескольких больших проблем. Не нужно будет перестраивать отчеты, из-за чего снижается нагрузка на сервер. Отчеты можно будет хранить в любом удобном вам месте, а клиентскую часть написать на комфортном для вас языке программирования.