← Назад к заметкам

Заметки

Поле типа Файл и поле типа Файл Диска: в чём разница

Чем отличается обычное файловое поле от поля Файл Диска: ID файла, объект Диска, прикреплённый объект и частые ошибки при получении ссылки.

У файлов в Битрикс24 легко перепутать несколько разных ID. Обычное поле Файл работает с загруженным файлом как с файловой записью. Поле Файл Диска связано с объектом Диска. А прикреплённый объект — это вообще отдельная запись связи между файлом Диска и задачей, комментарием, CRM-сущностью или другим объектом.

Задача

В пользовательских полях, задачах, комментариях и CRM можно встретить похожие файловые значения: просто ID файла, ID объекта Диска или ID прикрепления.

Из-за этого часто появляется ошибка: разработчик берёт число из поля, считает его ID файла и пытается получить путь через CFile::GetPath. Для обычного поля Файл это может сработать, а для файла Диска или прикреплённого объекта — уже нет.

Чтобы не путаться, полезно разделять три сущности:

  • обычный файл — запись файловой системы Битрикса;
  • объект Диска — файл как документ в модуле Диск;
  • прикреплённый объект — связь файла Диска с задачей, CRM, комментарием или другим объектом.

Поле типа Файл

Обычное поле типа Файл не связано с Диском. При загрузке через REST в него обычно передают файл в виде Base64.

Для универсальных методов CRM файл в такое поле можно передать массивом, где первый элемент — имя файла, второй — содержимое в Base64:

BX24.callMethod(
    'crm.item.add',
    {
        entityTypeId: 128,
        fields: {
            title: 'Элемент с файлом',
            ufCrm_123456: [
                'contract.pdf',
                'base64_encoded_content_here',
            ],
        },
    },
    function (result) {
        if (result.error()) {
            console.error(result.error());

            return;
        }

        console.log(result.data());
    }
);

Если поле множественное, передаётся массив таких файлов:

BX24.callMethod(
    'crm.item.add',
    {
        entityTypeId: 128,
        fields: {
            title: 'Элемент с несколькими файлами',
            ufCrm_123456: [
                [
                    'contract.pdf',
                    'base64_1',
                ],
                [
                    'act.pdf',
                    'base64_2',
                ],
            ],
        },
    },
    function (result) {
        if (result.error()) {
            console.error(result.error());

            return;
        }

        console.log(result.data());
    }
);

В коробке такой файл обычно можно получить через CFile, если у вас есть именно ID файловой записи:

use Bitrix\Main\Loader;

const FILE_ID = 123;

Loader::includeModule('main');

$file = CFile::GetByID(FILE_ID)->Fetch();

echo '<pre>';
print_r([
    'ID' => $file['ID'] ?? null,
    'FILE_NAME' => $file['FILE_NAME'] ?? null,
    'CONTENT_TYPE' => $file['CONTENT_TYPE'] ?? null,
    'SRC' => CFile::GetPath(FILE_ID),
]);
echo '</pre>';

Такой подход подходит для обычных файловых пользовательских полей, старых свойств инфоблоков, картинок товаров и похожих сценариев, где файл хранится как обычный файл, а не как документ Диска.

Поле типа Файл Диска

Поле типа Файл Диска, или Файл (диск), работает иначе. Оно связано с модулем Диск, поэтому значение относится к объекту Диска.

В такое поле нельзя просто положить Base64 так же, как в обычное файловое поле. Сначала файл загружают на Диск:

BX24.callMethod(
    'disk.folder.uploadfile',
    {
        id: 15,
        data: {
            NAME: 'contract.pdf',
        },
        fileContent: [
            'contract.pdf',
            'base64_encoded_content_here',
        ],
    },
    function (result) {
        if (result.error()) {
            console.error(result.error());

            return;
        }

        console.log(result.data());
    }
);

После загрузки с файлом можно работать как с объектом Диска. В REST для этого есть, например, метод disk.file.get:

BX24.callMethod(
    'disk.file.get',
    {
        id: 9043,
    },
    function (result) {
        if (result.error()) {
            console.error(result.error());

            return;
        }

        console.log(result.data());
    }
);

Ответ по файлу Диска может содержать не только ID объекта Диска, но и внутренний FILE_ID, размер, имя, ссылку на скачивание и другие данные.

В коробке можно получить объект Диска через D7:

use Bitrix\Main\Loader;
use Bitrix\Disk\File;

const DISK_OBJECT_ID = 9043;

Loader::includeModule('disk');

$disk_file = File::getById(DISK_OBJECT_ID);

if (!$disk_file) {
    echo 'Файл Диска не найден';

    return;
}

echo '<pre>';
print_r([
    'DISK_OBJECT_ID' => $disk_file->getId(),
    'NAME' => $disk_file->getName(),
    'FILE_ID' => $disk_file->getFileId(),
]);
echo '</pre>';

Здесь DISK_OBJECT_ID — это ID объекта Диска. А FILE_ID — внутренняя файловая запись, которая лежит ниже уровнем. Эти значения не нужно смешивать.

Прикреплённый объект

Прикреплённый объект — это не сам файл. Это запись связи, которая соединяет файл Диска с другой сущностью.

Например, файл может быть прикреплён к задаче, комментарию, сообщению или CRM-объекту. В таком случае в поле или методе может вернуться не ID файла, а ID прикрепления.

В REST информацию о такой связи можно получить через disk.attachedObject.get:

BX24.callMethod(
    'disk.attachedObject.get',
    {
        id: 423,
    },
    function (result) {
        if (result.error()) {
            console.error(result.error());

            return;
        }

        console.log(result.data());
    }
);

В коробке похожую задачу можно решить через Bitrix\Disk\AttachedObject:

use Bitrix\Main\Loader;
use Bitrix\Disk\AttachedObject;

const ATTACHED_OBJECT_ID = 423;

Loader::includeModule('disk');

$attached_object = AttachedObject::getById(ATTACHED_OBJECT_ID);

if (!$attached_object) {
    echo 'Прикреплённый объект не найден';

    return;
}

$disk_file = $attached_object->getFile();

if (!$disk_file) {
    echo 'Файл Диска не найден';

    return;
}

echo '<pre>';
print_r([
    'ATTACHED_OBJECT_ID' => $attached_object->getId(),
    'DISK_OBJECT_ID' => $disk_file->getId(),
    'FILE_ID' => $disk_file->getFileId(),
    'NAME' => $disk_file->getName(),
]);
echo '</pre>';

Такая цепочка важна: сначала прикреплённый объект, потом файл Диска, потом уже внутренний файловый ID или ссылка. Если сразу передать ID прикрепления в CFile::GetPath, результат может быть пустым.

Как не перепутать ID

Главная ошибка — видеть число и сразу считать, что это ID файла для CFile. В файловых сценариях Битрикса это не всегда так.

Например, такой код может не сработать, если 423 — это ID прикреплённого объекта, а не ID файла:

const ATTACHED_OBJECT_ID = 423;

echo CFile::GetPath(ATTACHED_OBJECT_ID);

Правильная логика другая:

// ATTACHED_OBJECT_ID — это ID связи.
// Сначала нужно получить прикреплённый объект,
// потом файл Диска,
// потом внутренний FILE_ID или ссылку через инструменты Диска.

Если непонятно, с каким типом поля вы работаете, сначала получите описание полей:

BX24.callMethod(
    'crm.item.fields',
    {
        entityTypeId: 128,
    },
    function (result) {
        if (result.error()) {
            console.error(result.error());

            return;
        }

        console.log(result.data());
    }
);

Для обычного поля Файл думайте в сторону Base64 при загрузке и CFile при работе в коробке. Для поля Файл Диска думайте в сторону объекта Диска и методов disk.*. Для файлов, прикреплённых к задачам, комментариям и другим объектам, отдельно учитывайте ID прикрепления.

Проще всего держать в голове такую цепочку: attached object — это связь, disk object — это файл в Диске, file ID — это внутренняя файловая запись. В разных методах может вернуться любой из этих уровней.