Как загрузить большой файл несколькими одновременными блоками и сохранить их в один файл в Python? - PullRequest
0 голосов
/ 18 октября 2019

Я использую PyCurl, диапазон http заголовка и потоки Python, поэтому, если мне нужно загрузить файл объемом 1 ГБ и я хочу использовать, например, 5 подключений к серверу, чтобы ускорить процесс, я просто делю 1 ГБ на пять частей,и создать пять потоков, которые загружают 1/5 на поток, сохраняют эту 1/5 в файл ".part", а когда все 5 потоков завершены и загружают файл 1/5, я просто соединяю все части и воссоздаю1 ГБ файл.

Мой вопрос: как я могу загрузить файл в тех же 5 чанках, но вместо того, чтобы сохранить каждый чанк во временный файл, а затем объединить все файлы в исходный файл, просто скачай 5 частей и сохраничасти к файлу 1gb напрямую? Это возможно?

Ответы [ 2 ]

2 голосов
/ 18 октября 2019

Итак, как мы обсуждали в комментариях, вы не можете действительно делать то, что вы пытаетесь сделать. Однако есть пара идей, которые могут вам помочь.

Вариант 1

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

with open("some_file_name", "wb") as f:
    f.truncate(some_size)

или

with open("some_file_name", "wb") as f:
    f.seek(some_size - 1)
    f.write('\0')

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

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

with open("some_file_name", "r+b") as f:
    f.seek(offset)
    f.write(data_chunk)

Вариант 2

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

пример: если вы решили, что хотите использовать только 1 ГБ и хотите загрузить большой файл кусками по 250 МБ

  • Начните загружать первые 4 блока параллельно.
  • Когда блок 1 завершает загрузку, вы можете записать его на диск и начать загрузку фрагмента 5.
  • , если блок завершает загрузку до того, как младший блок (например, 2 заканчивает до 1), удерживает его в памяти до следующего блокаотделка
  • это ограничивает использование памяти до 1 ГБ, поскольку не более четырех блоков по 250 МБ загружаются одновременно

Опция 3

запись во временные файлы (каквы упомянули в исходном вопросе), но храните их в месте, где пользователь, вероятно, их не увидит, например, во временном каталоге вашей системы или в скрытом каталоге, созданном вашей программой

1 голос
/ 18 октября 2019

Вы должны быть в состоянии сделать это легко, по крайней мере, в системе Unix / Linux. Ключ в том, что вы должны создать файл один раз в потоке 1, открыв его для записи. Затем вам нужно повторно открыть файл еще раз N (также для записи, но не для добавления), чтобы вы получили независимый дескриптор файла для каждого используемого потока.

Затем направьте каждый поток lseek в соответствующую начальную позицию в файле, используя собственный дескриптор файла, и затем он может начать запись фрагментов в файл, полученный с сервера. (Вы должны иметь возможность использовать стандартные файловые объекты Python и их метод seek, если вы используете двоичный ввод / вывод.)

Это работает, потому что каждый дескриптор файла имеет независимый указатель файла, иво всех системах Unix указатель файла должен автоматически перемещаться по мере написания. Кроме того, нет проблем с наличием пробелов в файле;Операционная система должна обработать это нормально.

IOW, для файла объемом 1 ГБ с 5 потоками:

thread 1, fd 1, position 0 (writing through 200MB-1)
thread 2, fd 2, position 200MB (through 400MB-1)
...
thread 5, fd 5, position 800MB (through 1GB-1)

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

with open("the_file", "r+b") as myfile:   # open for update
    myfile.seek(my_starting_pos)
    while ...:
         next_chunk = recv_from_server()
         myfile.write(next_chunk)
...