Окна сторожевого таймера Python ждут завершения копирования

Я использую модуль сторожевого таймера Python на сервере Windows 2012 для отслеживания новых файлов, появляющихся на общем диске. Когда сторожевой таймер замечает новый файл, он запускает процесс восстановления базы данных.

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

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

Что происходит, когда несколько файлов копируются на общий диск одновременно?

Вот мой код

import time
import subprocess
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class NewFile(FileSystemEventHandler):
    def process(self, event):
        if event.is_directory:
            return

    if event.event_type == 'modified':            
        if getext(event.src_path) == 'gz':
            load_pgdump(event.src_path)

    def on_modified(self, event):
        self.process(event)

def getext(filename):
    "Get the file extension"
    file_ext = filename.split(".",1)[1]
    return file_ext

def load_pgdump(src_path):    
    restore = 'pg_restore command ' + src_path
    subprocess.call(restore, shell=True)

def main():
    event_handler = NewFile()
    observer = Observer()
    observer.schedule(event_handler, path='Y:\\', recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

if __name__ == '__main__':
    main()

person tjmgis    schedule 19.08.2015    source источник
comment
Как вы передаете файл? Можете ли вы также загрузить контрольную сумму? Идея восстановления БД без него меня пугает. Если вы идете по маршруту проверка, задержка, еще раз проверка, вы идете с оптимизмом.   -  person    schedule 15.10.2015


Ответы (8)


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

Предлагая более простой цикл:

historicalSize = -1
while (historicalSize != os.path.getsize(filename)):
  historicalSize = os.path.getsize(filename)
  time.sleep(1)
print "file copy has now finished"
person Mtl Dev    schedule 12.12.2016
comment
Я думаю, что это сработает, если вы будете смотреть только один файл. Если вы просматриваете каталог, у вас может быть несколько чередующихся событий on_modified для разных файлов. Тогда логика будет немного сложнее. - person autra; 21.11.2019

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

copying = True
size2 = -1
while copying:
    size = os.path.getsize('name of file being copied')
    if size == size2:
        break
    else:
        size2 = os.path.getsize('name of file being copied')
        time.sleep(2)
person iri    schedule 19.08.2015

Я использую следующий код, чтобы дождаться копирования файла (только для Windows):

from ctypes import windll
import time

def is_file_copy_finished(file_path):
    finished = False

    GENERIC_WRITE         = 1 << 30
    FILE_SHARE_READ       = 0x00000001
    OPEN_EXISTING         = 3
    FILE_ATTRIBUTE_NORMAL = 0x80

    if isinstance(file_path, str):
        file_path_unicode = file_path.decode('utf-8')
    else:
        file_path_unicode = file_path

    h_file = windll.Kernel32.CreateFileW(file_path_unicode, GENERIC_WRITE, FILE_SHARE_READ, None, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, None)

    if h_file != -1:
        windll.Kernel32.CloseHandle(h_file)
        finished = True

    print 'is_file_copy_finished: ' + str(finished)
    return finished

def wait_for_file_copy_finish(file_path):
    while not is_file_copy_finished(file_path):
        time.sleep(0.2)

wait_for_file_copy_finish(r'C:\testfile.txt')

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

Наслаждаться ;)

person Dmytro    schedule 09.10.2017
comment
Это надежно поддерживает копирование между протоколом удаленного рабочего стола Windows (RDP). RDP создает обработчик файла, а затем считывает полное содержимое файла в память перед записью, что делает обычно рекомендуемую проверку размера файла каждые 0,x секунд ненадежной для больших файлов. Спасибо! - person Ty Hitzeman; 19.11.2020
comment
Документ для вызова CreateFileW от windll: docs.microsoft. com/en-us/windows/win32/api/fileapi/ - person Ty Hitzeman; 19.11.2020

Недавно у меня была похожая проблема с сторожевым псом. Довольно простой, но не очень умный обходной путь для меня состоял в том, чтобы проверить изменение размера файла в цикле while, используя список из двух элементов, один для «прошлого», один для «сейчас». Как только значения сравняются, копирование будет завершено.

Изменить: что-то вроде этого.

past = 0
now = 1
value = [past, now]
while True:
    # change

    # test
    if value[0] == value[1]:
        break
    else:
        value = [value[1], value[0]]
person jake77    schedule 19.08.2015

Это работает для меня. Протестировано в Windows, а также с python3.7

while True:
        size_now = os.path.getsize(event.src_path)
        if size_now == size_past:
            log.debug("file has copied completely now size: %s", size_now)
            break
            # TODO: why sleep is not working here ?
        else:
            size_past = os.path.getsize(event.src_path)
            log.debug("file copying size: %s", size_past)
person sathish    schedule 29.03.2019

Старый я знаю, но недавно я придумал решение именно этой проблемы. В моем случае меня интересовали только файлы wav и mp3. Эта функция гарантирует, что только файлы, которые полностью скопированы, будут отправлены в makerCore(), потому что созданные файлы-заполнители не имеют расширения и всегда будут иметь статус «не готов». Как только файл будет завершен, он снова запустит модуль сторожевого таймера, но на этот раз с расширением. Это также будет работать с несколькими файлами одновременно.

def on_created(event):
    #print(event)
    if str(event.src_path).endswith('.mp3') or str(event.src_path).endswith('.wav'):
        makerCore(event)
    else:
        print('not ready')
person CLipp    schedule 07.06.2021

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

Просто было «в процессе» к имени файла, пока копирование не будет завершено, а затем переименовать файл. Затем у вас может быть цикл while, ожидающий существования файла с именем без «в процессе», и все в порядке.

person Yohan Obadia    schedule 18.06.2018

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

    file_done = False
    file_size = -1

    while file_size != os.path.getsize(file_path):
        file_size = os.path.getsize(file_path)
        time.sleep(1)

    while not file_done:
        try:
            os.rename(file_path, file_path)
            file_done = True
        except:
            return True
person b3d    schedule 20.02.2020