# Data Flow for Sending Media Files in Telegram Desktop: A Complete Technical Guide

> Explore the seven-stage data flow for sending media files in Telegram Desktop. Understand file preparation, upload, MTProto, and MTP messages for seamless media sharing.

- Repository: [Telegram Desktop/tdesktop](https://github.com/telegramdesktop/tdesktop)
- Tags: deep-dive
- Published: 2026-04-05

---

**Telegram Desktop sends media files through a seven-stage pipeline that prepares the file, uploads it to Telegram's servers, constructs an MTProto inputMedia object, and transmits it via MTPmessages_SendMedia before processing the server's Updates response.**

The data flow for sending media files like photos, documents, and voice messages in Telegram Desktop follows a strict pipeline from UI selection to network transmission. This article examines the exact implementation in the `telegramdesktop/tdesktop` repository, tracing how user-selected files become encrypted MTProto messages. Understanding this data flow is essential for developers contributing to the client or building compatible implementations.

## Overview of the Media Sending Pipeline

When a user attaches a media file, the application executes a well-defined sequence across multiple subsystems:

1. **UI Capture** – The attach widget captures file selection and triggers preparation
2. **Media Preparation** – Files are validated, classified, and wrapped in a `PreparedList`
3. **Upload Streaming** – `Storage::Uploader` streams file parts to Telegram's upload infrastructure
4. **Input Media Construction** – Uploaded files are converted to `MTP_inputMedia*` objects
5. **Request Assembly** – `ApiWrap::sendMedia` serializes captions, entities, and send options
6. **Network Dispatch** – `Histories::sendPreparedMessage` transmits `MTPmessages_SendMedia`
7. **Response Handling** – Server-returned `MTPupdates` are applied to the local history

## Step-by-Step Data Flow Analysis

### 1. UI File Selection and Media Preparation

The process begins in [`Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp). When the user presses **Send**, the preview widget invokes the preparation layer to transform raw file paths into upload-ready structures.

The UI layer does not handle validation directly; instead, it delegates to `Storage::PrepareMediaList` to ensure consistent handling across all entry points.

### 2. File Validation and Metadata Extraction

The core preparation logic resides in [`Telegram/SourceFiles/storage/storage_media_prepare.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/storage/storage_media_prepare.cpp). The `PrepareMediaList` function validates each path, classifies the media type, and extracts essential metadata:

```cpp
PreparedList PrepareMediaList(
        const QStringList &files,
        int previewWidth,
        bool premium);

```

This function performs several critical operations:
- Validates local file existence, non-empty paths, and size limits
- Classifies files as photos, videos, music, or generic documents
- Extracts dimensions and MIME types via `FileLoadTask::ReadMediaInformation`
- Returns a `PreparedList` containing `PreparedFile` objects that drive the subsequent upload

Files that fail validation are flagged with specific error codes, allowing the UI to present appropriate feedback before network operations begin.

### 3. Uploading to Telegram Servers

Once prepared, files enter the upload subsystem in [`Telegram/SourceFiles/storage/file_upload.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/storage/file_upload.cpp). The `Uploader` class manages the actual data transmission:

- `Uploader::Entry` determines whether to treat the file as a photo, document, or audio, selecting appropriate part containers (`fileparts` vs `thumbparts`)
- The uploader handles multipart streaming, retry logic, and encryption of file segments
- Upon completion, `Uploader::photoReady` or `Uploader::documentReady` emits an `UploadedMedia` signal

The upload layer operates asynchronously, emitting completion signals that trigger the next phase without blocking the UI thread.

### 4. Constructing MTProto Input Media Objects

When the upload completes, [`Telegram/SourceFiles/apiwrap.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/apiwrap.cpp) receives the `UploadedMedia` signal and converts the uploaded data into MTProto-compatible structures. Two specialized functions handle this conversion:

```cpp
void ApiWrap::sendUploadedPhoto(
        FullMsgId fullId,
        const UploadedPhoto &info,
        const Api::SendOptions &options);

void ApiWrap::sendUploadedDocument(
        FullMsgId fullId,
        const UploadedDocument &info,
        const Api::SendOptions &options);

```

These functions construct `MTP_inputMediaPhoto` or `MTP_inputMediaDocument` objects, embedding the file references returned by Telegram's upload servers. Both functions ultimately forward to the generic `sendMedia` method, ensuring uniform handling of captions, entities, and send options regardless of media type.

### 5. Building the Send Media Request

The `ApiWrap::sendMedia` function in [`Telegram/SourceFiles/apiwrap.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/apiwrap.cpp) serves as the central coordinator for assembling the final network payload:

```cpp
void ApiWrap::sendMedia(
        not_null<HistoryItem*> item,
        const MTPInputMedia &media,
        Api::SendOptions options,
        Fn<void(bool)> done);

```

This method:
- Generates a cryptographically secure `randomId` and registers it via `registerMessageRandomId`
- Serializes the message caption, entities, and formatting
- Encodes send options including silent mode, scheduling, message effects, and paid star transactions
- Delegates to the history subsystem for actual transmission

### 6. History Subsystem and Network Transmission

The final network dispatch occurs in [`Telegram/SourceFiles/data/data_histories.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_histories.cpp) via `Histories::sendPreparedMessage`:

```cpp
int Histories::sendPreparedMessage(
        History *history,
        const ReplyTo *replyTo,
        uint64 randomId,
        MTP::Request<MTPmessages_SendMedia> request,
        Fn<void(const MTPUpdates&, const MTP::Response&)> done,
        Fn<void(const MTP::Error&, const MTP::Response&)> fail);

```

This function:
- Inserts a pending message into the local history cache for immediate UI feedback
- Dispatches the `MTPmessages_SendMedia` request through the MTProto connection layer
- Registers success and failure callbacks to handle the asynchronous server response

The history subsystem maintains message state throughout the transmission lifecycle, ensuring that pending messages survive application restarts and network interruptions.

### 7. Server Response and UI Updates

Upon successful transmission, the server returns `MTPupdates` containing the finalized message objects. The client processes these updates in [`Telegram/SourceFiles/history/history.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/history/history.cpp):

- `History::addNewMessage` creates concrete `HistoryItem` instances (photos, documents, voice notes) from the server data
- The pending local message is replaced with the authoritative server version
- UI widgets receive update notifications and repaint to reflect the confirmed message state

## Practical Implementation: Sending a Photo Programmatically

The following example demonstrates the complete data flow when sending a photo from a custom action:

```cpp
void sendMyPhoto(
        not_null<Main::Session*> session,
        const QString &filePath,
        const QString &caption) {

    // 1️⃣ Prepare the media list (single file → photo)
    const auto list = Storage::PrepareMediaList(
            QStringList{filePath},
            st::sendMediaPreviewSize, // preview width from style
            session->premium());      // premium flag

    if (list.error != Storage::PreparedList::Error::None) {
        LOG(("Failed to prepare media: %1").arg(list.error));
        return;
    }

    // 2️⃣ Upload via the uploader (creates an UploadedMedia signal)
    session->uploader().upload(
        std::make_shared<Storage::FilePrepareResult>(list.files.front()),
        [&](const Storage::UploadedMedia &media) {
            // 3️⃣ Build a HistoryItem that will be sent
            const auto item = session->data().createItem(
                session->data().peer( // target chat
                    session->user().id),
                QString(), // empty text, media will be attached
                { .options = {} });

            // 4️⃣ Call ApiWrap – the uploader already called sendUploadedPhoto,
            //    but we can also call it ourselves:
            session->api().sendUploadedPhoto(
                item->fullId(),
                media.photoInfo,
                Api::SendOptions()); // default options
        });
}

```

This snippet directly utilizes `Storage::PrepareMediaList` for validation, initiates upload via `Uploader`, and invokes `ApiWrap::sendUploadedPhoto` upon completion. All thumbnail generation, multipart streaming, and MTProto serialization occur within the framework's internal pipeline.

## Summary

- **Preparation Phase**: `Storage::PrepareMediaList` in [`storage_media_prepare.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/storage_media_prepare.cpp) validates files and extracts metadata before any network activity begins.
- **Upload Phase**: The `Uploader` class in [`file_upload.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/file_upload.cpp) handles encrypted multipart uploads and emits `UploadedMedia` signals upon completion.
- **API Construction**: `ApiWrap` methods (`sendUploadedPhoto`, `sendUploadedDocument`) convert uploaded data into `MTP_inputMedia*` objects for the MTProto protocol.
- **Network Dispatch**: `Histories::sendPreparedMessage` in [`data_histories.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/data_histories.cpp) transmits `MTPmessages_SendMedia` requests and manages pending message states.
- **Response Handling**: Server `MTPupdates` are processed by the history subsystem to finalize message objects and update the UI.

## Frequently Asked Questions

### How does Telegram Desktop handle file uploads for large media files?

Large files are automatically divided into parts by the `Uploader` class in [`Telegram/SourceFiles/storage/file_upload.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/storage/file_upload.cpp). The system streams these parts sequentially to Telegram's upload servers, with `Uploader::Entry` managing separate containers for file data and thumbnail previews. This multipart approach allows transmission to resume after network interruptions without re-uploading completed segments.

### What is the difference between sendUploadedPhoto and sendUploadedDocument?

`sendUploadedPhoto` constructs an `MTP_inputMediaPhoto` object optimized for image files with automatic compression and dimension metadata, while `sendUploadedDocument` creates an `MTP_inputMediaDocument` suitable for files requiring preservation of exact bytes, such as PDFs, voice messages, or video files. Both functions reside in [`Telegram/SourceFiles/apiwrap.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/apiwrap.cpp) and converge on the common `sendMedia` path after constructing their respective input media types.

### How does the MTProto API receive media messages from Telegram Desktop?

The client transmits media via the `MTPmessages_SendMedia` method, constructed in [`Telegram/SourceFiles/data/data_histories.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_histories.cpp) by `sendPreparedMessage`. This request includes the `MTPInputMedia` reference (pointing to the uploaded file on Telegram's servers), message caption, entities, and flags for options like silent sending or scheduling. The MTProto layer encrypts this payload and delivers it to Telegram's datacenters.

### Where is the media upload state managed in the Telegram Desktop codebase?

Upload state is primarily managed by the `Storage::Uploader` class in [`Telegram/SourceFiles/storage/file_upload.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/storage/file_upload.cpp), which tracks progress for each `Uploader::Entry`. The history subsystem in [`Telegram/SourceFiles/data/data_histories.cpp`](https://github.com/telegramdesktop/tdesktop/blob/main/Telegram/SourceFiles/data/data_histories.cpp) maintains the association between pending local messages and their corresponding upload operations, ensuring that UI state remains synchronized with background transmission progress.