Форма отправлена, в коде стоит CFile::SaveFile, а на выходе ноль и ни одной строки в таблице b_file? Чаще всего виноваты не "битрикс", а пустой $_FILES после лимита post_max_size=8M или отсутствие MODULE_ID в массиве файла. Ниже – как на CMS "1С-Битрикс: Управление сайтом" принять файл из формы, пройти цепочку MakeFileArray → SaveFile, привязать ID к инфоблоку или UF и отладить типичные сбои.
Класс CFile – стандартный способ зарегистрировать файл в ядре CMS: массив как у $_FILES, затем SaveFile с MODULE_ID и подпапкой в /upload. Успех – только целочисленный ID больше нуля и открывающийся SRC. При замене картинки передавайте old_file и del, иначе в b_file останется мусор. Это не Bitrix24: портальный Disk и REST Base64 здесь не используются.
В поиске часто попадают гайды про диск Bitrix24 – это другой продукт. Здесь только CMS: PHP, b_file и /upload. Update картинки – в гайде по Update, свойство "Файл" – в материале про свойства товаров, UF file – в статье про UF.
Разберите, когда нужен CFile на CMS, а не ручная заливка в /upload
CFile регистрирует файл в b_file и на диске /upload. Без него файл не привяжется к инфоблоку, UF или модулю. FTP подходит для статики, не для форм.
| Способ | Где файл живёт | Когда выбирать | Типичная ошибка |
|---|---|---|---|
| CFile::SaveFile | b_file + /upload/subdir | Форма, свойство, UF, свой модуль | SaveFile вернул 0 без MODULE_ID |
| Медиабиблиотека (админка) | Тот же b_file | Редактор контента без кода | Нет привязки к вашему API |
| FTP в /upload | Только диск | Статика, миграция архива | Нет ID в b_file |
| Диск CRM-портала / REST | Облако портала | CRM, не CMS | Копируют Base64 из helpdesk |
Делайте: для кода в своём модуле всегда идите через CFile. Не делайте: не смешивайте примеры disk.folder.uploadfile из CRM-портала с CMS – у них другая схема хранения.
Проверьте upload_dir, права на /upload и лимиты PHP до отладки CFile
Перед SaveFile проверьте окружение – иначе отладка CFile займёт час, хотя виноват php.ini. На тестовом стенде типичная картина: файл 12 МБ, post_max_size=8M, $_FILES пустой, а разработчик копает SaveFile.
- Узнайте upload_dir:
COption::GetOptionString('main', 'upload_dir', 'upload')– обычно это каталог /upload в корне сайта. - Проверьте права на /upload и подпапку (например iblock/abc): веб-сервер должен писать файлы (часто 755 на папку, 644 на файлы).
- Сверьте php.ini:
post_max_sizeдолжен быть не меньшеupload_max_filesize. Иначе при большом файле PHP "молча" обнулит и $_POST, и $_FILES. - Убедитесь, что форма с файлом имеет
enctype="multipart/form-data"– без этого $_FILES пустой. - Задайте MODULE_ID – код модуля-владельца (iblock, main, vendor.module) – иначе SaveFile часто возвращает false.
- Запустите
bitrix_server_test.phpиз дистрибутива – тест загрузки покажет права и лимиты до вашего кода.
Делайте: при пустом $_FILES сначала смотрите CONTENT_LENGTH и post_max_size, а не CFile. Не делайте: не вызывайте SaveFile с сырым путём на диск без MakeFileArray – ядро ждёт структуру как у $_FILES (name, size, tmp_name, type).
Соберите массив файла через CFile::MakeFileArray
MakeFileArray строит массив для SaveFile. path – ID файла, путь на сервере или URL.
$arFile = $_FILES['DOCUMENT'];
$arFile = CFile::MakeFileArray('/var/www/site/import/price.pdf');
$arFile = CFile::MakeFileArray(12345);
$arFile = CFile::MakeFileArray('https://example.com/image.jpg');
Для пути на диске (не ID) с версии 12.5.7 передайте skipInternal = true. Без tmp_name добавьте content и name вручную. Делайте: вызовите CheckFile или CheckImageFile до SaveFile. Не делайте: не передавайте только имя без tmp_name – получите false на форуме разработчиков.
Сохраните файл через CFile::SaveFile и удалите старый при замене
SaveFile кладёт файл в подпапку upload и пишет строку в b_file. Успех – только ID больше нуля.
$arFile = $_FILES['PREVIEW_PICTURE'];
$arFile['MODULE_ID'] = 'iblock';
// Замена: старый ID 999, удалить после успеха
$arFile['old_file'] = 999;
$arFile['del'] = 'Y';
$fileId = CFile::SaveFile($arFile, 'iblock/custom');
if (intval($fileId) > 0) {
// Привязка к элементу – см. гайд по Update
CIBlockElement::Update($elementId, [
'PREVIEW_PICTURE' => CFile::MakeFileArray($fileId),
]);
} else {
// Диагностика: права, CheckFile, MODULE_ID, tmp_name
}
Свойство "Файл" – через PROPERTY_VALUES в гайде по Update. UF file – через $USER_FIELD_MANAGER в статье про UF. Удаление: CFile::Delete($oldId) или old_file + del в SaveFile.
Делайте: проверяйте intval($fileId) > 0 сразу после SaveFile. Не делайте: не оставляйте старый file ID в свойстве при ручной заливке нового файла без del – диск зарастёт дублями.
Отладите типичные сбои: SaveFile=0, пустой $_FILES, MIME
Таблица быстрой диагностики – что проверить, если цепочка оборвалась.
| Симптом | Вероятная причина | Что сделать |
|---|---|---|
| $_FILES пустой | post_max_size меньше размера тела запроса | Увеличить post_max_size и upload_max_filesize, проверить enctype |
| SaveFile = 0 / false | Нет MODULE_ID или битый массив | Добавить MODULE_ID, полный name/tmp_name/type/size |
| Файл не на диске | Нет прав на SUBDIR в /upload | Права 755 на папку, bitrix_server_test.php |
| CheckFile отклонил | Расширение или размер вне лимита | Ослабить лимит или сменить расширение в настройках |
| ID есть, SRC 404 | Файл удалили вручную с FTP | Перезалить или очистить битую запись в b_file |
Делайте: логируйте CheckFile и размер $_FILES до SaveFile. Не делайте: не считайте успехом файл в /upload без ID в b_file.
Прочитайте метаданные через GetFileArray и D7 FileTable
После сохранения путь – CFile::GetPath($id) или SRC из GetFileArray. Для пакетной выборки по списку ID удобнее D7, чем GetFileArray в цикле:
use Bitrix\Main\FileTable;
$rows = FileTable::getList([
'filter' => ['@ID' => [101, 102, 103]],
'select' => ['ID', 'ORIGINAL_NAME', 'SUBDIR', 'FILE_NAME'],
])->fetchAll();
// Ресайз картинки – по-прежнему CFile
$thumb = CFile::ResizeImageGet($fileId, ['width' => 200, 'height' => 200]);
FileTable читает b_file; новые файлы регистрируйте через SaveFile, не FileTable::add. Ресайз картинок – CFile::ResizeImageGet. В цикле по сотням элементов соберите ID и один getList вместо GetFileArray на каждой итерации – так вы избежите лишней нагрузки на диск.
Получите рабочий результат: чек-лист после загрузки
Перед выкладкой на прод пройдите список – так вы фиксируете, что цепочка $_FILES → MakeFileArray → SaveFile → привязка сработала.
- SaveFile вернул ID больше нуля.
- Строка в b_file с MODULE_ID и SUBDIR на месте.
- GetPath($id) открывается в браузере без 404.
- Старый file ID удалён при замене (del + old_file).
- Свойство инфоблока или UF показывает файл в форме редактирования.
Все пункты выполнены – загрузка работает. Обработчик можно вынести в свой модуль с тем же MODULE_ID.
Нужна помощь с формой загрузки в каталоге или кастомным свойством "Файл" – обсудим задачу и разберём стенд. Похожие кейсы по инфоблокам – в портфолио.
Автор: Максим Мольков, разработчик 1С-Битрикс.
Источники: справочник CFile, CFile::SaveFile, урок "Загрузка файлов", документация PHP по загрузке файлов.
Частые вопросы
Чем CFile отличается от прямой записи файла в /upload?
CFile регистрирует файл в таблице b_file и связывает его с модулями, свойствами и UF. Прямая запись на диск даёт файл без ID – компоненты и API инфоблока его не увидят. Для любой формы на CMS используйте MakeFileArray и SaveFile.
Как сохранить файл из $_FILES в свойство инфоблока?
Добавьте MODULE_ID в массив, вызовите SaveFile с подпапкой iblock, получите ID и передайте его в CIBlockElement::Update в PROPERTY_VALUES или в PREVIEW_PICTURE/DETAIL_PICTURE. Пошагово – в гайде по Update элемента инфоблока.
Почему CFile::SaveFile возвращает 0?
Проверьте MODULE_ID, полноту массива (name, tmp_name, type, size), результат CheckFile, права на /upload и лимиты post_max_size/upload_max_filesize. Пустой $_FILES при большом файле почти всегда означает превышение post_max_size, а не ошибку ядра.
Как удалить старый файл при обновлении картинки?
Перед SaveFile укажите old_file со старым ID и del = Y – ядро удалит предыдущую запись после успешной записи новой. Альтернатива – CFile::Delete($oldId) до Update, если новый файл уже сохранён отдельно.
Где на диске лежат загруженные через CFile файлы?
В каталоге upload_dir (по умолчанию /upload) в подпапке, которую вы передали вторым аргументом SaveFile, плюс SUBDIR из b_file. Точный веб-путь – CFile::GetPath($id) или поле SRC в GetFileArray.
Нужен ли FileTable::add вместо SaveFile?
Для новой загрузки с диска или из формы – нет, используйте CFile::SaveFile. FileTable удобен для чтения и массовой выборки по ID из b_file; физическое сохранение в /upload делает именно CFile.