Перейти к содержимому

Файлы из кода - сохранение, уменьшенные копии, удаление

Кладём файл в хранилище платформы, показываем его и убираем за собой, не оставляя за спиной каталог загрузок на десятки гигабайт.

Решение

Сохраняем файл в хранилище:

$fileId = CFile::SaveFile(CFile::MakeFileArray($tmpPath), 'vendor.shop');
// второй аргумент - подкаталог хранения, по нему потом видно, чьи это файлы
$element->Update($id, ['PROPERTY_VALUES' => ['DOC' => $fileId]]);
// в поле или свойство кладут номер файла, а не путь к нему

Хранилище возвращает номер файла, и именно он живёт в полях и свойствах. Платформа сама решает, в какой каталог его положить, и это освобождает вас от раскладки файлов по подкаталогам.

Собираем путь для вывода:

$path = CFile::GetPath($fileId); // путь для ссылки на странице
$info = CFile::GetFileArray($fileId); // и всё остальное: имя, размер, тип
// пустой ответ означает, что запись о файле уже удалена из таблицы
printf('<a href="%s">%s</a>', $path, htmlspecialchars($info['ORIGINAL_NAME']));

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

Готовим уменьшенную копию:

$thumb = CFile::ResizeImageGet(
$fileId,
['width' => 300, 'height' => 300],
BX_RESIZE_IMAGE_PROPORTIONAL,
true // копия создаётся при первом обращении и потом переиспользуется
);
echo '<img src="' . $thumb['src'] . '" loading="lazy">';

Копия нужного размера создаётся при первом обращении к ней. Десяток разных размеров на одну картинку - это десяток файлов на диске, поэтому набор размеров стоит держать коротким и одинаковым по всему сайту.

Убираем файл за собой:

CFile::Delete($fileId);
// файлы полей и свойств платформа убирает вместе с элементом сама
// а вот сохранённое своим кодом удаляет только ваш код

Файл, сохранённый вашим кодом, удаляет тоже ваш код. Иначе каталог загрузок растёт годами, и однажды резервная копия перестаёт помещаться туда, куда её складывают.

Загруженное посетителем проверяют до сохранения, а не после. Каталог загрузок доступен снаружи, и попавший в него исполняемый файл превращается в чужой вход на сайт.

Файлы для служебных надобностей стоит держать вне каталога загрузок. Выгрузки с персональными данными и журналы обмена там доступны любому, кто угадает имя.

Имя файла от посетителя не переносят на диск как есть. Платформа приводит его к безопасному виду, а вот свой код с прямой записью файла эту работу пропускает.

Типичные проблемы

Файл сохраняется, а на странице не виден.

В поле записан путь вместо номера файла из хранилища. Путь собирают по номеру уже в момент вывода.

Сохранение падает без внятной ошибки.

У каталога загрузок нет прав на запись для владельца процесса. Права на этот каталог проверяют самым первым делом при любом разборе.

Каталог загрузок вырос до десятков гигабайт.

Файлы сохранялись своим кодом и никогда не удалялись. Удаление такого файла - обязанность того же кода.

Картинки на витрине занимают гигабайты копий.

Уменьшенные копии одной и той же картинки делаются в десятке разных размеров. Набор размеров держат коротким и общим для всего сайта, а не для страницы.

В каталоге загрузок оказался чужой скрипт.

Тип загружаемого файла не проверялся вашим кодом перед его сохранением на диск. Проверку делают до записи, а не после неё.

Частые вопросы

Можно ли хранить файлы вне хранилища платформы?

Да, если это служебные файлы и вы сами следите за их чисткой. Пользовательские удобнее держать в хранилище.

Как отдать файл только авторизованным?

Хранить его вне доступного снаружи каталога и отдавать скриптом с проверкой прав. Ссылка на каталог загрузок такой проверки не даёт.

Что делать со старыми уменьшенными копиями?

Чистить их вместе со сменой набора размеров. Иначе на диске остаются копии, которые никто не запрашивает.

Как перенести файлы между стендами?

Отдельно от базы и отдельно от кода. Каталог загрузок в репозиторий не кладут: он растёт непредсказуемо.

Смежное

Первоисточник