Найти - Пользователи
Полная версия: Копирование файлов
Начало » Python для новичков » Копирование файлов
1 2
Patrik
Добрый день.

Копирую файлы функцией shutil.copy

Может кто подскажет алгоритм для вывода статуса копирования (оставшееся время и процент завершения задачи)?
john123
Если Вы хотите получать статус в процессе копирования одного файла, то с этой функцией, скорее всего этого не получится, т.к. Вы никак не сможете понять, сколько байт уже скопировалось, да и вызов функции похоже блокирующий.

Если же нужно копировать несколько файлов (именно этой функцией), то алгоритм такой:
1. Считаете общее количество файлов, которое предстоит скопировать;
2. 100% делите на общее количество файлов и получаете число процентов на один файл;
3. Поочередно копируете файлы и обновляете статус в процентах (прибавляете кол-во процентов на один файл)

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

Если всё-таки нужно получать количество процентов именно при копировании одного файла, смотрите в сторону функции shutil.copyfileobj(). В ней, вроде как, можно переопределить объект файла, а в этом объекте уже можно будет получать количество скопированных байт.

Для вывода прогрессбара (или другой статистики) в консоли, можете воспользоваться модулем python-progressbar, либо подсмотрите как это сделано здесь: http://code.activestate.com/recipes/577871-python-progressbar/
Вкратце: символ \r осуществляет возврат каретки без переноса строки, позволяя тем самым перерисовывать свою статистику поверх старой.
Patrik
john123: Я планирую копировать файлы по локальной сети, преимущественно все файлы большие (несколько Гб.).

Вывод хоть какой-то информации о копировании нужно сделать, чтобы понимать работает приложение или подвисло.

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

Примеров работы функции shutil.copyfileobj() я к сожалению не нашел.

Может для решения данной задачи проще воспользоваться чем-то сторонним?
К примеру Robocopy, но тогда от него будет вылетать консоль.
john123
В случае с одним большим файлом принцип будет точно такой же. Только заместо числа “общее количество файлов” подставьте размер файла в байтах (либо сумму размеров всех файлов в байтах).

Patrik
Я планирую копировать файлы по локальной сети, преимущественно все файлы большие (несколько Гб.).
А по какому сетевому протоколу планируете копировать? И в какой операционной системе?

Вообще самый простой способ копирования, например, указан вот здесь:
http://stackoverflow.com/a/1903753

Фактически копирование “вручную”, т.е. открывается файл-источник и файл-приемник, из источника в цикле считывается определенное кол-во байт (блок) и записывается в приемник.
При этом Вы получаете полный контроль над процессом, но вот скорость копирования в теории может пострадать. Но это уже нужно будет вручную настраивать, подбирая оптимальный размер блока.

В общем расскажите поподробнее, почему Вы хотите использовать shutil и по каким протоколам и в каких операционных системах это всё будет происходить.
Patrik
john123: Протокол smb, операционная система Windows 7 и выше.
Модуль shutil решил использовать, так как в нем есть функции копирования.

Программа в зависимости от выбора пользователя начинает копировать папки с файлами из сетевой папки по протоколу SMB. Преимущественно все файлы большие. Если просто запустить shutil.copy, то пользователю становится не понятно работает или зависла программа.
В принципе идея считать общее количество файлов и выделять процент выполненного копирования мне понравился. Но если копировать одни большой файл, то информативность теряется.
py.user.next
Скорее всего, в одном потоке качать, в другом - смотреть, сколько закачалось.
john123
Похоже, что shutil не поддерживает возможности удаленного копирования. Точнее в ОС Windows функция CreateFile (которая открывает файловый дескриптор), конечно, поддерживает открытие файла по SMB, но это будет работать только в Windows. В Linux, например, придется использовать сторонние библиотеки.

Если Вы будете запускать программу только в Windows, то воспользуйтесь тем решением, которое я приводил:
john123
Вообще самый простой способ копирования, например, указан вот здесь:
http://stackoverflow.com/a/1903753
Оно как раз должно подойти. Попробуйте.
Только файл target у Вас будет открываться вот так (UNC):
target = open("//HOST/path/to/file")
Если что-то не понятно, задавайте вопросы
Patrik
john123: Попробовал предложенный вами скрипт, почему-то не работает:
print('\r%02d%%' % (copied * 100 / source_size))
проценты пишет, но в столбик. Т.е. символ \r каретку не возвращает, соответственно статистику не перерисовывает.
Мне показалось, что процесс копирования очень долго идет, хотя поиграться с параметром копирования блока я не успел.

По аналогии можно сделать и копирование нескольких файлов сделав функцию с параметрами, что копируем и куда.
Единственное, что я слабо представляю, что делать с папками.
john123
Для правильного вывода в одну строчку попробуйте вот так (проверено в Windows):
sys.stdout.write('\r%02d%%' % (copied * 100 / source_size))
sys.stdout.flush()

Patrik
Мне показалось, что процесс копирования очень долго идет, хотя поиграться с параметром копирования блока я не успел.
Да, в этом случае попробуйте поставить размер блока побольше. Например поставьте число 65535.

Patrik
По аналогии можно сделать и копирование нескольких файлов сделав функцию с параметрами, что копируем и куда.
Да. Просто перед началом копирования Вам нужно будет посчитать и сложить размеры всех копируемых файлов. Эта сумма как раз и будет равняться прогрессу в 100 процентов.

Patrik
Единственное, что я слабо представляю, что делать с папками.
А вот тут Вам придется проверять существование всех вложенных папок и, если каталог не создан, создать его.
Попробуйте воспрользоваться функцией os.makedirs() - https://docs.python.org/2/library/os.html#os.makedirs.

Т.е. будет что-то вроде:
file_dir = os.path.dirname(TARGET_FILENAME)
if not os.path.exists(file_dir):
    os.makedirs(file_dir)
И после этого копируете файлы.
Patrik
john123: Начал писать скрипт копирование.
Логика скрипта такая.
Делаю проверку, если копируется файл, то считаю размер файла, формирую ссылку для копирования в папку.
Создаю промежуточные папки, если они есть.
Передаю файл на копирование.

Иначе копируется папка.
Создаю промежуточные папки.
Считаю общий размер и количество файлов.
Передаю файлы на копирование.

import os, sys
def copyFiles(SRC_PATH, DST_PATH):
   
    total_files = 0                 # всего файлов
    total_size = 0                  # размер файла/ов
   
    if os.path.isfile(SRC_PATH):    # если в SRC_PATH копирется файл
        total_size = os.stat(SRC_PATH).st_size # считаем размер файла
        srcpath = SRC_PATH
        dstpath = os.path.join(DST_PATH, os.path.basename(SRC_PATH)) # формируем путь, куда копируем
        total_files += 1            # счётчик файлов
        file_dir = os.path.dirname(dstpath) # обрезаем имя файла
        if not os.path.exists(file_dir):    # проверяем существует ли путь
            os.makedirs(file_dir)           # создаем недостающие папки
      
        print('copy', srcpath, 'to', dstpath)
       
       
    else:                           # иначе в SRC_PATH копируется папка
        for root, dirs, files in os.walk(SRC_PATH): # проходим по SRC_PATH рекурсивно
            for mkdir in root:
                print('Папки для создания', root)
       
        for root, dirs, files in os.walk(SRC_PATH): # проходим по SRC_PATH рекурсивно
            for name in files:
                srcpath = os.path.join(root, name)    # получаем полное имя файла для его копирования
                dirr = os.path.relpath(root, SRC_PATH) # относительный путь
                dstpath = os.path.normpath(os.path.join(DST_PATH, dirr, name)) # формируем путь, куда копируем
                total_size += os.path.getsize(srcpath)# считаем размер файла     
                total_files += 1 # счетчик файлов
                print('copy', srcpath, 'to', dstpath)
    print('total files:', total_files)
    print('total size', total_size)

столкнулся со следующими трудностями:
1 Если файл копируется в папку, то все в порядке. Но если его придется копировать под другим именем,
как сделать шаблон?

2 При копировании папки, нужно создавать промежуточные директории.
for root, dirs, files in os.walk(SRC_PATH): # проходим по SRC_PATH рекурсивно
    for mkdir in root:
          print('Папки для создания', root)

выдает много дубликатов, и не понятно как сопоставить с dstpath.

3 Как сделать связку с :

copied = 0
source = open(srcpath, 'rb')
target = open(dstpath, 'wb')
while True:
    chunk = source.read(32768)
    if not chunk:
        break
    target.write(chunk)
    copied += len(chunk)
    sys.stdout.write('\r%02d%%' % (copied * 100 / source_size))
    sys.stdout.flush()
source.close()
target.close()
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB