Telegram Desktop File Download Upload Manager Architecture Explained

Telegram Desktop separates file transfers into two dedicated subsystems—Storage::Uploader for uploads and Storage::DownloadManagerMtproto for downloads—both operating over MTProto with per-DC session pools, priority queues, and reactive progress streams.

The telegramdesktop/tdesktop repository implements a highly concurrent file transfer system that handles millions of daily uploads and downloads. This architecture lives inside each Main::Session and provides automatic load balancing, CDN support, and seamless integration with the app's reactive UI framework.

Architecture Overview

The system maintains separate pipelines for upload and download operations, each optimized for their specific direction of data flow.

Aspect Upload Pipeline Download Pipeline
Entry Point session->uploader() returns a Storage::Uploader object session->downloader() returns a Storage::DownloadManagerMtproto object
Task Representation Uploader::Entry stores parts, progress counters, and MD5 state DownloadMtprotoTask (derived from FileLoader) manages per-file state
Queue Strategy Single FIFO queue (std::vector<Entry> _queue) Per-DC priority queue (DownloadManagerMtproto::Queue)
Session Management Dynamic pool of upload sessions with byte budgets (kMaxUploadPerSession) Fixed pool per DC (up to kMaxSessionsCount) with balance tracking (DcBalanceData)
Part Size Variable based on file size (kDocumentUploadPartSize0…4), supports big files Fixed at kDownloadPartSize = 128 KB as defined in storage/file_download.h
Premium Limits Emits nonPremiumDelays() for slow-upload UI prompts Emits nonPremiumDelays() for download-speed-up UI prompts

Upload Pipeline Deep Dive

Entry Point and Task Representation

Uploads begin when the client calls session->uploader().upload(fullMsgId, filePrepareResult) as implemented in Telegram/SourceFiles/storage/file_upload.cpp (lines 291-312). The Uploader creates an internal Entry structure (lines 73-84) that tracks:

  • The prepared file data and MIME type
  • Part boundaries and MD5 computation state
  • Progress counters for emission to the UI
// From file_upload.cpp - Entry creation
session->uploader().upload(uploadId, file);

Queue Management and Session Handling

The Uploader maintains a single FIFO queue processed by Uploader::maybeSend(). This method dynamically manages a pool of DC indices through chooseDcIndexForNextRequest and removeDcIndex (lines 15-30 in file_upload.cpp).

Each session operates under a byte budget defined by kMaxUploadPerSession. When a DC index is selected, sendPart() assembles either a sliced part request (for photos/thumbnails) or a document part request, constructing the appropriate MTProto call:

  • MTPupload_SaveFilePart for standard files
  • MTPupload_SaveBigFilePart for large files exceeding the standard part thresholds

Progress and Completion

When the server responds, Uploader::partLoaded() updates progress counters, modifies Data::UploadState in the model, and fires events through rpl::event_stream (_photoProgress, _documentProgress). Upon completion of all parts (maybeFinishFront() → finishFront()), the system emits an UploadedMedia struct via _photoReady or _documentReady, triggering the final API call (api()->sendUploadedPhoto or api()->sendUploadedDocument).

Download Pipeline Deep Dive

Task Initialization and Queue Registration

Downloads utilize mtpFileLoader, a subclass of both FileLoader and DownloadMtprotoTask. Instantiation follows this pattern as shown in Telegram/SourceFiles/storage/file_download_mtproto.cpp:

auto loader = std::make_unique<mtpFileLoader>(
    session,
    StorageFileLocation{ fileId, accessHash, dcId, … },
    Data::FileOrigin::FromMessage(messageId),
    QString(),          // write-to-file path (empty = memory only)
    /*loadSize=*/0,     // 0 → full size
    /*fullSize=*/0,
    LocationType::Unknown,
    LoadToCacheAsWell,
    LoadFromCloud::Yes,
    /*autoLoading=*/true,
    /*cacheTag=*/Data::kDocumentCacheTag);

Calling FileLoader::startLoading() invokes DownloadMtprotoTask::addToQueue(), registering the task with DownloadManagerMtproto (lines 38-45 in download_manager_mtproto.cpp).

Per-DC Session Balancing

Unlike uploads, downloads use a per-DC priority queue system. DownloadManagerMtproto::checkSendNext() iterates through each DC's Queue, selecting the highest-priority ready task and invoking task->loadPart(sessionIndex) (lines 78-80).

The manager maintains DcBalanceData structures that track:

  • Requested bytes per session
  • Success/failure ratios
  • Timeout events (sessionTimedOut)

Sessions are automatically added or removed based on load via changeRequestedAmount and requestSucceeded.

CDN Support and Error Handling

When a download encounters a CDN redirect (MTPupload_fileCdnRedirect), DownloadMtprotoTask::switchToCDN (lines 78-88 in download_manager_mtproto.cpp) transitions the task to CDN parameters. This flow re-uploads pending parts, validates per-chunk SHA-256 hashes, and retries missing parts automatically.

Progress updates emit through FileLoader::updates(), while failures trigger cancelOnFail(). When a task completes, it calls removeFromQueue() and the manager emits taskFinished() for UI consumption.

Integration with the Main Session and UI

Both subsystems are owned by the main session as shown in Telegram/SourceFiles/main/main_session.cpp:

_downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get())),
_uploader(/* inside Main::Session via ApiWrap */)

UI components subscribe to reactive streams for real-time updates:

  • Upload progress: session->uploader().documentReady(), session->uploader().photoReady(), and session->uploader().nonPremiumDelays()
  • Download progress: session->downloaderTaskFinished() and per-loader FileLoader::updates()

The Upload Progress Overlay and Download Bar components in Telegram/SourceFiles/ui/effects/upload_progress_overlay.cpp and related files consume these streams to render progress indicators.

Practical Code Examples

Uploading a Photo

FullMsgId uploadId = { peer, localMessageId };
auto file = FilePrepareResult::CreatePhoto(
    QByteArray::fromRawData(imageData, imageSize),
    "photo.jpg",
    mimeType,
    options);
session->uploader().upload(uploadId, file);

Downloading with Progress Tracking

auto loader = std::make_unique<mtpFileLoader>(
    session,
    StorageFileLocation{ fileId, accessHash, dcId },
    Data::FileOrigin::FromMessage(messageId),
    QString(), 0, 0, LocationType::Unknown,
    LoadToCacheAsWell, LoadFromCloud::Yes, true,
    Data::kDocumentCacheTag);

loader->updates() | rpl::start_with_next([=](auto &&) {
    qDebug() << "Progress:" << loader->currentProgress();
});
loader->start();

Global Download Completion Handler

session->downloaderTaskFinished()
    | rpl::start_with_next([=] {
        ui->downloadBar->hide();
    });

Summary

  • Dual-subsystem design: Telegram Desktop uses Storage::Uploader for uploads and Storage::DownloadManagerMtproto for downloads, both residing in Main::Session.
  • Dynamic session pools: Uploads manage a flexible pool with byte budgets per DC, while downloads use fixed pools with automatic balancing based on success rates and timeouts.
  • Queue strategies: Uploads use a single FIFO queue; downloads implement per-DC priority queues for optimal bandwidth utilization.
  • CDN integration: The download pipeline fully supports CDN redirects with automatic hash validation and retry logic.
  • Reactive UI binding: Both systems expose rpl::event_stream interfaces (documentReady(), downloaderTaskFinished()) for real-time progress updates and premium limit notifications.

Frequently Asked Questions

What file size limits trigger big file uploads in Telegram Desktop?

Large files activate the big file upload path when they exceed the thresholds defined in kDocumentUploadPartSize0 through kDocumentUploadPartSize4 constants in storage/file_upload.cpp. When this occurs, the uploader switches from MTPupload_SaveFilePart to MTPupload_SaveBigFilePart and represents the file as MTP_inputFileBig rather than MTP_inputFile.

How does Telegram Desktop handle slow network conditions during downloads?

The DownloadManagerMtproto monitors each session's performance through DcBalanceData structures that track requested bytes, successful completions, and timeout events. When sessionTimedOut() fires or success rates drop, changeRequestedAmount() automatically redistributes load across available sessions. For non-premium users hitting speed limits, the system emits nonPremiumDelays() signals to trigger UI prompts for speed upgrades.

What is the fixed download part size in Telegram Desktop?

According to storage/file_download.h, Telegram Desktop uses a fixed 128 KB (kDownloadPartSize = 128 * 1024) part size for all downloads, regardless of file type or size. This differs from uploads, which use variable part sizes based on total file dimensions.

Can uploads and downloads operate simultaneously without blocking?

Yes. Because the architectures are separate subsystems owning independent session pools and queues, uploads via Storage::Uploader and downloads via Storage::DownloadManagerMtproto execute concurrently. The per-DC session limits (kMaxUploadPerSession for uploads, kMaxSessionsCount for downloads) ensure that file transfers in one direction do not starve the other of network resources.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →