Setting up bulk uploads of product photos in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Bulk Product Photo Upload Configuration in 1C-Bitrix

A catalog of 20,000 items where each product needs its main photo replaced and a gallery of 5–8 images added. Through the admin interface, that's several person-weeks of manual work. In practice, the task is solved in a few hours using the Bitrix API and batch processing.

Where Photos Are Stored in Bitrix

Product image files are stored in the b_file table. Each file has its own ID. The iblock (catalog) references them via properties:

  • PREVIEW_PICTURE — announcement image, stored in b_iblock_element.PREVIEW_PICTURE (FK to b_file.ID)
  • DETAIL_PICTURE — detail image, b_iblock_element.DETAIL_PICTURE
  • A property of type "File/Image" — b_iblock_element_property.VALUE referencing b_file.ID

Multiple images (gallery) — via a property of type "Image" with the "Multiple" flag. For trade offers — the same fields in the context of the trade offer element.

Preparing Files

Images must be prepared in advance: directory structure by SKU, consistent naming. A convenient scheme:

/import/images/
  sku_001/
    main.jpg
    2.jpg
    3.jpg
  sku_002/
    main.jpg

If files come from 1C — names typically match the XML_ID of the element. If from a photo studio — a mapping file is needed (CSV: SKU → file names).

Uploading via CFile and CIBlockElement

The basic approach: read a CSV with the mapping, for each row upload the file via CFile::SaveFile() and update the element.

$csvRows = parseCsv('/import/mapping.csv'); // [['xml_id' => 'sku_001', 'files' => ['main.jpg', '2.jpg']]]

foreach ($csvRows as $row) {
    // Find element by XML_ID
    $element = \Bitrix\Iblock\ElementTable::getList([
        'filter' => ['XML_ID' => $row['xml_id'], 'IBLOCK_ID' => CATALOG_IBLOCK_ID],
        'select' => ['ID'],
    ])->fetch();

    if (!$element) continue;

    $imageDir = '/import/images/' . $row['xml_id'] . '/';
    $files = [];

    foreach ($row['files'] as $i => $filename) {
        $filePath = $imageDir . $filename;
        if (!file_exists($filePath)) continue;

        $fileId = \CFile::SaveFile([
            'name'       => $filename,
            'type'       => mime_content_type($filePath),
            'tmp_name'   => $filePath,
            'error'      => 0,
            'size'       => filesize($filePath),
        ], 'iblock');

        if ($i === 0) {
            // First file — main image
            \CIBlockElement::Update($element['ID'], [
                'PREVIEW_PICTURE' => \CFile::MakeFileArray($filePath),
                'DETAIL_PICTURE'  => \CFile::MakeFileArray($filePath),
            ]);
        } else {
            $files[] = ['VALUE' => \CFile::MakeFileArray($filePath)];
        }
    }

    // Multiple gallery property
    if (!empty($files)) {
        \CIBlockElement::SetPropertyValues($element['ID'], CATALOG_IBLOCK_ID, $files, 'MORE_PHOTO');
    }
}

CFile::MakeFileArray() does not copy the file — it is simply a descriptor array. CFile::SaveFile() performs the actual save and writes to b_file.

Performance at High Volumes

On a catalog of 20,000+ items, a direct loop will run for 2–4 hours and may fail due to a timeout or memory limit. A few rules:

Split into batches. Process 200–500 elements per iteration, saving progress to a file or table.

Disable unnecessary event handlers. During bulk updates, the OnBeforeIBlockElementUpdate and OnAfterIBlockElementUpdate events can trigger a chain of heavy operations (price recalculation, cache invalidation, search index update). Temporarily disable agents and events if they are not needed during import:

define('BX_DONT_PROCESS_EVENTS', true); // only if you fully understand the consequences
// OR selectively:
\Bitrix\Main\EventManager::getInstance()->removeEventHandler('iblock', 'OnAfterIBlockElementUpdate', $handlerId);

Use CLI scripts. Run via php -f import_images.php — no web request time limits. Set set_time_limit(0) and ini_set('memory_limit', '512M').

Clear cache after completion. After bulk upload, reset the iblock cache: \Bitrix\Iblock\InformationBlock::cleanTagCache($iblockId). Do not do this inside the loop — only once at the end.

Error Handling and Logging

$log = fopen('/var/log/image_import.log', 'a');

foreach ($csvRows as $row) {
    try {
        // ... processing
        fwrite($log, date('Y-m-d H:i:s') . " OK: {$row['xml_id']}\n");
    } catch (\Throwable $e) {
        fwrite($log, date('Y-m-d H:i:s') . " ERR: {$row['xml_id']} — {$e->getMessage()}\n");
    }
}

Common errors: file not found, invalid MIME type, duplicate in b_file (Bitrix checks by hash — re-uploading the same file will return the existing ID).

Thumbnail Generation

After upload, Bitrix generates thumbnails lazily — on first access via a component. To warm the cache immediately:

\CFile::ResizeImageGet($fileId, ['width' => 400, 'height' => 400], BX_RESIZE_IMAGE_PROPORTIONAL, true);

Or via a CLI utility if ImageMagick is configured on the server.

Estimated Timelines

Catalog Size Estimated Time
Up to 1,000 products 1–2 hours (setup + upload)
1,000–10,000 products 4–8 hours
10,000–50,000 products 1–2 days (including testing and cache warm-up)