Для передачи больших файлов в Python без загрузки их целиком в оперативную память необходимо использовать потоковую передачу (streaming). Это позволяет отправлять данные частями, что критично при работе с файлами размером в несколько гигабайт или при ограниченной памяти. Основной инструмент для этого — функция `shutil.copyfileobj`, которая копирует данные из одного буфера в другой порциями. Альтернативой является разбиение файла на сегменты или использование сжатия на лету, если размер данных позволяет.

Выбор подхода к передаче больших файлов

При работе с файлами объемом более 100 МБ стандартное чтение `f.read()` или `open().read()` приводит к исчерпанию оперативной памяти. Решение — потоковая передача.

Используйте `shutil.copyfileobj` для копирования данных порциями (обычно по 64 КБ). Это позволяет передавать файл, не загружая его полностью в RAM.

Если файл очень большой и сеть медленная, рассмотрите разбиение на сегменты. Это упрощает управление ошибками: при сбое можно перезагрузить только часть файла, а не весь поток.

Сжатие (gzip/zlib) уменьшает объем передаваемых данных, но требует вычислительных ресурсов. Применяйте его, если файл содержит много повторяющихся паттернов или текста, но избегайте для уже сжатых форматов (jpg, mp4).

Выбирайте метод исходя из приоритета: скорость (прямая передача), экономия трафика (сжатие) или надежность (разбиение + проверка).

Подготовка данных: проверка целостности и выбор формата

Перед началом передачи убедитесь, что исходный файл не поврежден. Используйте `hashlib` для вычисления контрольной суммы (MD5 или SHA-256) файла.

Сохраните хеш в отдельном файле или передайте его отдельно по защищенному каналу. Это позволит получателю проверить целостность данных после приема.

Выбирайте текстовые форматы (CSV, JSON) для сжатия. Бинарные данные лучше передавать без сжатия, чтобы избежать накладных расходов на распаковку и потери точности.

Оцените объем доступной памяти. Если файл больше 80% от свободного места в RAM, потоковая передача обязательна.

Убедитесь, что кодировка файлов совместима (например, UTF-8 для текстов), чтобы избежать ошибок при чтении/записи.

Реализация потоковой передачи (streaming)

Используйте `shutil.copyfileobj` для копирования данных из одного файла в другой порциями.

Пример кода: открывайте файлы в режиме бинарного чтения/записи (`'rb'`, `'wb'`). Укажите размер буфера (например, 65536 байт).

Функция возвращает количество переданных байт. Если нужно обработать данные на лету, используйте генераторы или цикл `while True` с проверкой возвращаемого значения.

Пример: передача файла через socket без загрузки в память. Используйте `copyfileobj` для отправки данных из локального файла в сокет.

Важно корректно закрывать файлы после завершения передачи, чтобы освободить ресурсы.

Использование сжатия на лету

Библиотеки `gzip` и `zlib` позволяют сжимать данные в процессе передачи.

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

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

Сжатие увеличивает время обработки. Если скорость сети высока, прямой поток может быть быстрее из-за отсутствия накладных расходов на распаковку у получателя.

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

Разбиение файла на части

Разбейте файл на сегменты (например, по 10 МБ). Это упрощает управление ошибками и позволяет возобновить передачу после сбоя.

Используйте `os.path.getsize` для определения общего размера и цикл с шагом, равным размеру сегмента.

Передавайте каждый сегмент отдельно, сохраняя метаданные (номер сегмента, размер, хеш).

Получатель собирает сегменты в исходный файл. После завершения проверяется общая контрольная сумма.

Этот подход полезен при нестабильных соединениях или при необходимости кэширования промежуточных результатов.

Обеспечение надежности передачи

Проверяйте целостность данных с помощью контрольных сумм (MD5, SHA-256). Вычислите хеш перед отправкой и после приема.

Используйте механизмы повторной отправки при ошибках. Если передача сегмента не удалась, перезапустите только этот блок.

Логгируйте ошибки передачи. Это поможет диагностировать проблемы с сетью или дисками.

Для критически важных данных используйте протоколы с подтверждением приема (например, TCP поверх сокетов).

Не полагайтесь на автоматическое восстановление: всегда проверяйте результат.

Оптимизация сетевых параметров

Настройте буферы чтения/записи. Увеличьте размер буфера, если сеть медленная, но не превышайте лимиты памяти.

Используйте таймауты (`socket.settimeout`) для предотвращения зависания при потере соединения.

Включите многопоточность: одна потоковая задача сжимает, другая передает. Это ускорит процесс на многоядерных системах.

Для локальной сети используйте `localhost` или прямые IP-адреса без прокси.

Избегайте лишних вызовов `flush()`, если не требуется немедленная отправка данных.

Управление ресурсами

Корректно закрывайте файлы и сокеты после завершения передачи. Используйте контекстные менеджеры (`with`), чтобы гарантировать закрытие.

Очищайте временные файлы (сегменты, кэши) после успешной передачи или при ошибке.

Мониторьте использование памяти во время работы. Если потребление растет, увеличьте размер буфера или уменьшите его.

Не оставляйте открытые сокеты без использования — это может привести к исчерпанию портов.

Вопросы и ответы

Как проверить целостность файла после передачи?

Вычислите контрольную сумму (MD5 или SHA-256) перед отправкой и сравните с хешем полученного файла. Используйте библиотеку `hashlib` для вычисления.

Когда стоит использовать сжатие вместо прямой передачи?

Сжатие целесообразно, если файл содержит много повторяющихся данных (текст, CSV) и сеть медленная. Для бинарных файлов (изображения, видео) сжатие неэффективно.

Как возобновить передачу после сбоя?

Разбейте файл на сегменты. Передавайте их последовательно. При ошибке перезапустите только поврежденный сегмент, а не весь файл.

import shutil

# Потоковая передача файла без загрузки в память
with open('input.txt', 'rb') as f_in:
    with open('output.txt', 'wb') as f_out:
        shutil.copyfileobj(f_in, f_out, length=65536)

print('Файл передан успешно!')
import gzip
import hashlib

# Сжатие на лету и проверка целостности
def transfer_compressed(input_file, output_socket):
    with open(input_file, 'rb') as f_in:
        with gzip.GzipFile(fileobj=f_in, mode='rb') as gz_in:
            while True:
                chunk = gz_in.read(65536)
                if not chunk:
                    break
                output_socket.send(chunk)
    
    # Проверка целостности (пример)
    with open('output.txt', 'rb') as f_out:
        expected_hash = hashlib.sha256(f_out.read()).hexdigest()
    print(f'Хеш файла: {expected_hash}')