Форум сайта python.su
0
Добрый день.
Копирую файлы функцией shutil.copy
Может кто подскажет алгоритм для вывода статуса копирования (оставшееся время и процент завершения задачи)?
Офлайн
7
Если Вы хотите получать статус в процессе копирования одного файла, то с этой функцией, скорее всего этого не получится, т.к. Вы никак не сможете понять, сколько байт уже скопировалось, да и вызов функции похоже блокирующий.
Если же нужно копировать несколько файлов (именно этой функцией), то алгоритм такой:
1. Считаете общее количество файлов, которое предстоит скопировать;
2. 100% делите на общее количество файлов и получаете число процентов на один файл;
3. Поочередно копируете файлы и обновляете статус в процентах (прибавляете кол-во процентов на один файл)
Время копирования скорее всего придется вычислять уже в процессе самого копирования, замеряя примерное время, за которое скопируется минимальный блок байт, или целый файл.
Если всё-таки нужно получать количество процентов именно при копировании одного файла, смотрите в сторону функции shutil.copyfileobj(). В ней, вроде как, можно переопределить объект файла, а в этом объекте уже можно будет получать количество скопированных байт.
Для вывода прогрессбара (или другой статистики) в консоли, можете воспользоваться модулем python-progressbar, либо подсмотрите как это сделано здесь: http://code.activestate.com/recipes/577871-python-progressbar/
Вкратце: символ \r осуществляет возврат каретки без переноса строки, позволяя тем самым перерисовывать свою статистику поверх старой.
Отредактировано john123 (Июнь 27, 2014 16:55:19)
Офлайн
0
john123: Я планирую копировать файлы по локальной сети, преимущественно все файлы большие (несколько Гб.).
Вывод хоть какой-то информации о копировании нужно сделать, чтобы понимать работает приложение или подвисло.
Идея с несколькими файлами мне понравилась, но если вдруг окажется нужно будет скопировать один большой файл, она будет не информативна.
Примеров работы функции shutil.copyfileobj() я к сожалению не нашел.
Может для решения данной задачи проще воспользоваться чем-то сторонним?
К примеру Robocopy, но тогда от него будет вылетать консоль.
Офлайн
7
В случае с одним большим файлом принцип будет точно такой же. Только заместо числа “общее количество файлов” подставьте размер файла в байтах (либо сумму размеров всех файлов в байтах).
PatrikА по какому сетевому протоколу планируете копировать? И в какой операционной системе?
Я планирую копировать файлы по локальной сети, преимущественно все файлы большие (несколько Гб.).
Отредактировано john123 (Июнь 28, 2014 17:05:31)
Офлайн
0
john123: Протокол smb, операционная система Windows 7 и выше.
Модуль shutil решил использовать, так как в нем есть функции копирования.
Программа в зависимости от выбора пользователя начинает копировать папки с файлами из сетевой папки по протоколу SMB. Преимущественно все файлы большие. Если просто запустить shutil.copy, то пользователю становится не понятно работает или зависла программа.
В принципе идея считать общее количество файлов и выделять процент выполненного копирования мне понравился. Но если копировать одни большой файл, то информативность теряется.
Офлайн
857
Скорее всего, в одном потоке качать, в другом - смотреть, сколько закачалось.
Офлайн
7
Похоже, что shutil не поддерживает возможности удаленного копирования. Точнее в ОС Windows функция CreateFile (которая открывает файловый дескриптор), конечно, поддерживает открытие файла по SMB, но это будет работать только в Windows. В Linux, например, придется использовать сторонние библиотеки.
Если Вы будете запускать программу только в Windows, то воспользуйтесь тем решением, которое я приводил:
john123Оно как раз должно подойти. Попробуйте.
Вообще самый простой способ копирования, например, указан вот здесь:
http://stackoverflow.com/a/1903753
target = open("//HOST/path/to/file")
Отредактировано john123 (Июль 1, 2014 12:51:13)
Офлайн
0
john123: Попробовал предложенный вами скрипт, почему-то не работает:
print('\r%02d%%' % (copied * 100 / source_size))
Офлайн
7
Для правильного вывода в одну строчку попробуйте вот так (проверено в Windows):
sys.stdout.write('\r%02d%%' % (copied * 100 / source_size)) sys.stdout.flush()
PatrikДа, в этом случае попробуйте поставить размер блока побольше. Например поставьте число 65535.
Мне показалось, что процесс копирования очень долго идет, хотя поиграться с параметром копирования блока я не успел.
PatrikДа. Просто перед началом копирования Вам нужно будет посчитать и сложить размеры всех копируемых файлов. Эта сумма как раз и будет равняться прогрессу в 100 процентов.
По аналогии можно сделать и копирование нескольких файлов сделав функцию с параметрами, что копируем и куда.
PatrikА вот тут Вам придется проверять существование всех вложенных папок и, если каталог не создан, создать его.
Единственное, что я слабо представляю, что делать с папками.
file_dir = os.path.dirname(TARGET_FILENAME) if not os.path.exists(file_dir): os.makedirs(file_dir)
Офлайн
0
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)
for root, dirs, files in os.walk(SRC_PATH): # проходим по SRC_PATH рекурсивно for mkdir in root: print('Папки для создания', root)
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()
Отредактировано Patrik (Июль 4, 2014 15:31:56)
Офлайн