Уведомления

Группа в Telegram: @pythonsu

#1 Июнь 27, 2014 16:31:28

Patrik
От:
Зарегистрирован: 2011-04-21
Сообщения: 70
Репутация: +  0  -
Профиль   Отправить e-mail  

Копирование файлов

Добрый день.

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

Может кто подскажет алгоритм для вывода статуса копирования (оставшееся время и процент завершения задачи)?



Офлайн

#2 Июнь 27, 2014 16:49:45

john123
Зарегистрирован: 2013-12-22
Сообщения: 56
Репутация: +  7  -
Профиль   Отправить e-mail  

Копирование файлов

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

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

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

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

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

Отредактировано john123 (Июнь 27, 2014 16:55:19)

Офлайн

#3 Июнь 28, 2014 14:49:14

Patrik
От:
Зарегистрирован: 2011-04-21
Сообщения: 70
Репутация: +  0  -
Профиль   Отправить e-mail  

Копирование файлов

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

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

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

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

Может для решения данной задачи проще воспользоваться чем-то сторонним?
К примеру Robocopy, но тогда от него будет вылетать консоль.



Офлайн

#4 Июнь 28, 2014 16:51:04

john123
Зарегистрирован: 2013-12-22
Сообщения: 56
Репутация: +  7  -
Профиль   Отправить e-mail  

Копирование файлов

В случае с одним большим файлом принцип будет точно такой же. Только заместо числа “общее количество файлов” подставьте размер файла в байтах (либо сумму размеров всех файлов в байтах).

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

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

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

В общем расскажите поподробнее, почему Вы хотите использовать shutil и по каким протоколам и в каких операционных системах это всё будет происходить.

Отредактировано john123 (Июнь 28, 2014 17:05:31)

Офлайн

#5 Июнь 30, 2014 12:03:33

Patrik
От:
Зарегистрирован: 2011-04-21
Сообщения: 70
Репутация: +  0  -
Профиль   Отправить e-mail  

Копирование файлов

john123: Протокол smb, операционная система Windows 7 и выше.
Модуль shutil решил использовать, так как в нем есть функции копирования.

Программа в зависимости от выбора пользователя начинает копировать папки с файлами из сетевой папки по протоколу SMB. Преимущественно все файлы большие. Если просто запустить shutil.copy, то пользователю становится не понятно работает или зависла программа.
В принципе идея считать общее количество файлов и выделять процент выполненного копирования мне понравился. Но если копировать одни большой файл, то информативность теряется.



Офлайн

#6 Июнь 30, 2014 21:42:38

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10015
Репутация: +  857  -
Профиль   Отправить e-mail  

Копирование файлов

Скорее всего, в одном потоке качать, в другом - смотреть, сколько закачалось.



Офлайн

#7 Июль 1, 2014 12:49:19

john123
Зарегистрирован: 2013-12-22
Сообщения: 56
Репутация: +  7  -
Профиль   Отправить e-mail  

Копирование файлов

Похоже, что shutil не поддерживает возможности удаленного копирования. Точнее в ОС Windows функция CreateFile (которая открывает файловый дескриптор), конечно, поддерживает открытие файла по SMB, но это будет работать только в Windows. В Linux, например, придется использовать сторонние библиотеки.

Если Вы будете запускать программу только в Windows, то воспользуйтесь тем решением, которое я приводил:

john123
Вообще самый простой способ копирования, например, указан вот здесь:
http://stackoverflow.com/a/1903753
Оно как раз должно подойти. Попробуйте.
Только файл target у Вас будет открываться вот так (UNC):
target = open("//HOST/path/to/file")
Если что-то не понятно, задавайте вопросы

Отредактировано john123 (Июль 1, 2014 12:51:13)

Офлайн

#8 Июль 1, 2014 20:00:23

Patrik
От:
Зарегистрирован: 2011-04-21
Сообщения: 70
Репутация: +  0  -
Профиль   Отправить e-mail  

Копирование файлов

john123: Попробовал предложенный вами скрипт, почему-то не работает:

print('\r%02d%%' % (copied * 100 / source_size))
проценты пишет, но в столбик. Т.е. символ \r каретку не возвращает, соответственно статистику не перерисовывает.
Мне показалось, что процесс копирования очень долго идет, хотя поиграться с параметром копирования блока я не успел.

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



Офлайн

#9 Июль 2, 2014 10:02:36

john123
Зарегистрирован: 2013-12-22
Сообщения: 56
Репутация: +  7  -
Профиль   Отправить e-mail  

Копирование файлов

Для правильного вывода в одну строчку попробуйте вот так (проверено в 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)
И после этого копируете файлы.

Офлайн

#10 Июль 4, 2014 15:31:19

Patrik
От:
Зарегистрирован: 2011-04-21
Сообщения: 70
Репутация: +  0  -
Профиль   Отправить e-mail  

Копирование файлов

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()



Отредактировано Patrik (Июль 4, 2014 15:31:56)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version