Битрикс добавить файлы в архив и отдать на скачивание
Уже неоднократно сталкивался с задачей выведения пользователю на скачивание файлов, залитых в качестве свойств инфоблока. И если это самое свойство ставить с типом "Файл" то битрикс автоматически данному файлу присваивал уникальное имя, конфликта файлов в системе не возникало, но вот выдавать этот файл было крайне проблематично.
Старый мой пост, перенесен из моего блога на сайте 1С-Битрикс.
Уже неоднократно сталкивался с задачей выведения пользователю на скачивание файлов, залитых в качестве свойств инфоблока. И если это самое свойство ставить с типом "Файл" то битрикс автоматически данному файлу присваивал уникальное имя, конфликта файлов в системе не возникало, но вот выдавать этот файл было крайне проблематично.
До недавнего времени вместо свойства "Файл" ставил другое - "Привязка к файлу на сервере" и тогда по ИД файла вытягивалось его имя, файл выдавался на скачивание и все было просто замечательно.
Но, в один прекрасный момент, возникла задача и необходимость более профессионального подхода к данному вопросу и вот решил сделать полноценный механизм по закачке/скачке файлов.
Итак, сама задача:
Пользователь сайта через компонент добавления нового элемента ИБ добавляет свой элемент. Одним из свойств элемента является поле "Файл" с типом "Файл". Дальше этот элемент через вывод списка новостей выводим другим пользователям. Файл должен быть доступен для скачивания с тем именем, с которым его заливали, а не с той "кракозяброй", с которой он хранится на сервере.
Заливка файла проходит нормально, обычным образом, поэтому на ней останавливаться не буду.
Вывод элементов происходит с помощью стандартного компонента news.list. При выводе элементов, когда добираемся до свойства с файлом, получаем всего-лишь ИД файла и ничего больше.
Т.к. у меня таких файлов могло быть несколько, то данный блок получился такой:
Тут мы делаем перебор по всем файлам, добавленным в данное свойство элемента. Получаем файл по его ИД. Для отображения пользователю нормального, читабельного имени, получаем имя, с которым данный файл был загружен на сервер:
Дальше выводим ссылку пользователю на скачивание файла:
Скачивание файла не напрямую, а через другой вспомогательный файл со скриптом, было сделано сначала просто потому, что пользователю файл при сохранении выдавал все-таки не то имя, которое мы отображаем, а то, с которым он хранится на сервере и с этим надо было как-то бороться. Вот и пришел к решению, что нужно создать доп. файл, в котором будет определяться нужное имя файла. Этому скрипту передаем ИД файла, который нужно скачать.
Дальше дело переходит уже к скрипту.
Скрипт начинается со строки, в которой мы проверяем указан ли вообще ИД файла:
Дальше мы снова получаем оригинальное имя файла и транслитерируем его. В Данной транслитерации учтены символы русского и украинского языка. Кому нужны еще какие-то символы - добавляйте.
Для того, чтобы построить нужную нам схему html-файла, которая будет нам выдавать файл на скачивание, нужно указать соответствующие head-функции. Все данные для этих функций можно выбрать из свойств файла. Что в принципе и было сделано.
Обращаю особое внимание на строки:
Битриксовая функция получения пути файла выдает путь не физический (где файл лежит на сервере), а путь относительно корня сайта. поэтому к пути нужно добавить где сайт лежит физически на сервере.
Теперь построчно самый важный кусок всего скрипта:
Имя файла, которое будет выдано пользователю при сохранении:
Непосредственное чтение файла:
Уже после реализации, осознал несколько преимуществ такого скачивания.
Во-первых, это конечно же транслитерация названия файла. Она является достаточно важным моментом данной реализации, т.к. не все браузеры нормально реагируют на кириллицу.
Во-вторых, пользователь никак не может получить прямую ссылку на файл. И если внутри скрипта поставить доп. проверку на то, а может ли текущий пользователь скачивать этот файл, то воспользоваться прямой ссылкой без авторизации на сайте уже будет очень проблематично.
Была обнаружена критическая уязвимость в приведенном скрипте, рекомендую ознакомиться с выводами по уязвимости и принять свое собственное решение, что с этим делать. У меня, к сожалению или счастью, нет проектов, на которых есть необходимость в данном скрипте, но, возможно, кому-то будет полезно. содержит критическую уязвимость, поэтому в приведенном виде не может быть использован в конечных проектах.
Описание уязвимости:
1) перебором ID можно выкачать все загруженные файлы - а вы не можете знать, какие из них защищены по доступу
2) можно указать путь к файлу - т.к. к целому значению параметр не приводится, то скачается любой произвольный файл, включая dbconn.php с паролем к БД
К сожалению, этот уязвимый скрипт уже разошелся по ряду проектов. Если Вы лично имеете отношение к таким проектам, рекомендую срочно внести правки, либо удалить этот скрипт на них. Могу порекомендовать: 1) Передавать не только ID картинки, но и ID элемента инфоблока и проверять, что в этом элементе есть эта картинка 2) все параметры приводить к целому.
Как распаковать zip-архив я писал ранее , но принцип аналогичный.
UPD:
Что бы в результирующем архиве исключить путь до папки upload можно написать так:
UPD2:
Если нужно полностью исключить любые директории, то, похоже, придется копировать их во временную папку. Примерно так:
- createZipAndDownload($arPackFiles);
- function createZipAndDownload($arFiles)
- $zip = new ZipArchive();
- $filename = "system_sensor_archive_".date("d.m.Y").".zip";
- var_dump($filename);
- if ($zip->open($filename, ZipArchive::CREATE)!==TRUE)
- exit("Невозможно открыть \n");
- >
- foreach ($arFiles as $file)
- $arr = explode("/", $file);
- var_dump($arr);
- $zip->addFile($file, end($arr));
- >
- $zip->close();
- file_force_download($filename);
- >
- function file_force_download($file)
- if (file_exists($file))
- // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт
- // если этого не сделать файл будет читаться в память полностью!
- if (ob_get_level())
- ob_end_clean();
- >
- // заставляем браузер показать окно сохранения файла
- header('Content-Description: File Transfer');
- header('Content-Type: application/octet-stream');
- header('Content-Disposition: attachment; filename=' . basename($file));
- header('Content-Transfer-Encoding: binary');
- header('Expires: 0');
- header('Cache-Control: must-revalidate');
- header('Pragma: public');
- header('Content-Length: ' . filesize($file));
- // читаем файл и отправляем его пользователю
- readfile($file);
- unlink($file);
- exit;
- >
- >
**Вторая функция сразу отдает сформированный архив на скачивание
Ваш код разумный но подход к решению задачи не понравился. Сама идея того, что нужно создавать копии файлов и временную директорию, только для того, чтобы их скрыть из архива - абсурдна. Снизу ещё один листинг в копилку.
/*удалить файл если-создан*/
if (file_exists($_SERVER["DOCUMENT_ROOT"].$zipFileName)) unlink($_SERVER["DOCUMENT_ROOT"].$zipFileName);
>
// Массив со списком путей, до архивируемых файлов
foreach($arFiles as $iFileID) $arPackFiles[] = $_SERVER["DOCUMENT_ROOT"].CFile::GetPath($iFileID);
>
Уже неоднократно сталкивался с задачей выведения пользователю на скачивание файлов, залитых в качестве свойств инфоблока. И если это самое свойство ставить с типом "Файл" то битрикс автоматически данному файлу присваивал уникальное имя, конфликта файлов в системе не возникало, но вот выдавать этот файл было крайне проблематично.
До недавнего времени вместо свойства "Файл" ставил другое - "Привязка к файлу на сервере" и тогда по ИД файла вытягивалось его имя, файл выдавался на скачивание и все было просто замечательно.
Но, в один прекрасный момент, возникла задача и необходимость более профессионального подхода к данному вопросу и вот решил сделать полноценный механизм по закачке/скачке файлов.
Итак, сама задача:
Пользователь сайта через компонент добавления нового элемента ИБ добавляет свой элемент. Одним из свойств элемента является поле "Файл" с типом "Файл". Дальше этот элемент через вывод списка новостей выводим другим пользователям. Файл должен быть доступен для скачивания с тем именем, с которым его заливали, а не с той "кракозяброй", с которой он хранится на сервере.
Заливка файла проходит нормально, обычным образом, поэтому на ней останавливаться не буду.
Вывод элементов происходит с помощью стандартного компонента news.list. При выводе элементов, когда добираемся до свойства с файлом, получаем всего-лишь ИД файла и ничего больше.
Т.к. у меня таких файлов могло быть несколько, то данный блок получился такой:
Тут мы делаем перебор по всем файлам, добавленным в данное свойство элемента. Получаем файл по его ИД. Для отображения пользователю нормального, читабельного имени, получаем имя, с которым данный файл был загружен на сервер:
Скачивание файла не напрямую, а через другой вспомогательный файл со скриптом, было сделано сначала просто потому, что пользователю файл при сохранении выдавал все-таки не то имя, которое мы отображаем, а то, с которым он хранится на сервере и с этим надо было как-то бороться. Вот и пришел к решению, что нужно создать доп. файл, в котором будет определяться нужное имя файла. Этому скрипту передаем ИД файла, который нужно скачать.
Дальше дело переходит уже к скрипту.
Дальше мы снова получаем оригинальное имя файла и транслитерируем его. В Данной транслитерации учтены символы русского и украинского языка. Кому нужны еще какие-то символы - добавляйте.
Для того, чтобы построить нужную нам схему html-файла, которая будет нам выдавать файл на скачивание, нужно указать соответствующие head-функции. Все данные для этих функций можно выбрать из свойств файла. Что в принципе и было сделано.
Обращаю особое внимание на строки:
Битриксовая функция получения пути файла выдает путь не физический (где файл лежит на сервере), а путь относительно корня сайта. поэтому к пути нужно добавить где сайт лежит физически на сервере.
Теперь построчно самый важный кусок всего скрипта:
Имя файла, которое будет выдано пользователю при сохранении:
Уже после реализации, осознал несколько преимуществ такого скачивания.
Во-первых, это конечно же транслитерация названия файла. Она является достаточно важным моментом данной реализации, т.к. не все браузеры нормально реагируют на кириллицу.
Во-вторых, пользователь никак не может получить прямую ссылку на файл. И если внутри скрипта поставить доп. проверку на то, а может ли текущий пользователь скачивать этот файл, то воспользоваться прямой ссылкой без авторизации на сайте уже будет очень проблематично.
в конце файла - не нужно:)
Vadim Dumbravanu обнаружил уязвимость в приведенном скрипте, рекомендую ознакомиться с выводами по уязвимости и принять свое собственное решение, что с этим делать. У меня, к сожалению или счастью, нет проектов, на которых есть необходимость в данном скрипте, но, возможно, кому-то будет полезно.
Итак!
Скрипт содержит критическую уязвимость, поэтому в приведенном виде не может быть использован в конечных проектах. Описание уязвимости:
1) перебором ID можно выкачать все загруженные файлы - а вы не можете знать, какие из них защищены по доступу
2) можно указать путь к файлу - т.к. к целому значению параметр не приводится, то скачается любой произвольный файл, включая dbconn.php с паролем к БД
К сожалению, этот уязвимый скрипт уже разошелся по ряду проектов. Если Вы лично имеете отношение к таким проектам, рекомендую срочно внести правки, либо удалить этот скрипт на них.
Могу порекомендовать:
1) Передавать не только ID картинки, но и ID элемента инфоблока и проверять, что в этом элементе есть эта картинка
2) все параметры приводить к целому.
Подскажите как на лету (stream?) добавить файлы (директория с файлами) в zip архив, и отдать клиенту, не сохраняя временный файл архива, и если придёт запрос от другого клиента одновременно, это будет работать?
Для разных клиентов требуется добавлять разные файлы (у каждого клиента своя дир-я).
Если с разных ip запрашивается один скрипт, я понимаю что он не будет ожидать завершения первого?
- Вопрос задан более двух лет назад
- 591 просмотр
Вы в любом случае создаёте архив, отдаёте на скачивание клиенту и сразу удаляете. Собрать архив на лету и отдать на скачивание не получится.
Создать архив можно как-то так.
Затем отдаёте на скачивание клиенту. Далее вызываете функцию unlink , чтобы удалить временный zip.
IP тут вообще не причём. Вы работаете с обычными файлами. Генерируете уникальное название. Можно использовать time() и проблем не будет.
Остаётся отметить, что unset($file) не удалит временный файл на диске, его удалит команда unlink($file) . И $file должна указывать на реально существующую директорию, в которую есть права на запись пользователю, под которым запускается этот PHP-скрипт.
Подскажите Это тогда для чего ?
Алексей Денисов, ну почитайте для чего) Если вы будете сразу отдавать файл есть вероятность того, что может прерваться интернет во время загрузки. И файл до конца не будет скачан. Для медленного интернета и подобных обрывания сети есть возможность скачивать файлы не до конца. А потом нажать "продолжить загрузку" и файл будет загружаться не с начала, а с момента прерывания. Если вы будете отдавать сразу файл пользователю, то каждый раз при обрыве интернета ему придется скачивать заново.
Почему так? Просто в данный момент вы не авторизовались на сайте, или у вас не достаточно прав.
Итак, чтобы разрешить просматривать вложения из письма, зная что оно у всех откроется, необходима самая малость:
- Первым делом, в панели управления выбираем вкладку "Настройки" и переходим "Пользователи -> Группы пользователей". Проверяем чтобы у группы "Все пользователи (в том числе неавторизованные)" на вкладке "Доступ" для веб-форм было выставлено значение "по умолчанию" или "доступ закрыт".
- в панели управления выбираем вкладку "Сервисы" и переходим "Веб-формы -> Настройка форм". В нужной нам форме на вкладке "Доступ" выставляем для группы "Все пользователи (в том числе неавторизованные)" права "Работа со всеми результатами в соответствии с их статусами".
Теперь скачивать вложения по ссылке из письма можно без предварительной авторизации на сайте. В то же время, для просмотра заполненной формы на сайте, как и прежде нужен доступ к админке.
Если считаете статью полезной,
не ленитесь ставить лайки и делиться с друзьями.
Тема оформления админки для MaxSite CMS Система комментирования HyperComments для сайта На что обратить внимание при заказе сайта, чтобы не было мучительно больно. Если бы я знал это чуть раньше. Основные моменты и ошибки, связанные с индексированием сайтов. Сказ о хостинге Majordomo Как сделать качественный сайт и заставить его работать?
Комментариев: 1
Куда копать если ничего не помогает? полный доступ к форме всем пользователям. файл имеет размер 0 байт.
Читайте также: