gpt4 book ai didi

python - 在 Windows 上取消 python 中的停滞文件复制

转载 作者:太空宇宙 更新时间:2023-11-03 15:40:13 25 4
gpt4 key购买 nike

在 Windows 上,我想使用 Python 通过网络复制一堆文件。有时,网络没有响应,复制会停止。我想检查是否发生这种情况,并在发生这种情况时跳过有问题的文件。通过询问这个相关问题here ,我发现了CopyFileEx函数,允许使用回调函数,可以中止文件复制。

Python 中的实现如下所示:

import win32file

def Win32_CopyFileEx( ExistingFileName, NewFileName, Canc = False):
win32file.CopyFileEx(
ExistingFileName, # PyUNICODE | File to be copied
NewFileName, # PyUNICODE | Place to which it will be copied
Win32_CopyFileEx_ProgressRoutine, # CopyProgressRoutine | A python function that receives progress updates, can be None
Data = None, # object | An arbitrary object to be passed to the callback function
Cancel = Canc, # boolean | Pass True to cancel a restartable copy that was previously interrupted
CopyFlags = win32file.COPY_FILE_RESTARTABLE, # int | Combination of COPY_FILE_* flags
Transaction = None # PyHANDLE | Handle to a transaction as returned by win32transaction::CreateTransaction
)

从 CopyFileEx 函数的文档中,我可以看到取消正在运行的副本的两种可能性。

pbCancel [in, optional] If this flag is set to TRUE during the copy operation, the operation is canceled. Otherwise, the copy operation will continue to completion.

我不知道如何做到这一点。我尝试再次使用相同的文件句柄调用相同的函数,但将取消标志设置为 TRUE,但这会导致错误,因为相关文件正在被另一个进程使用。

另一种可能性似乎是回调函数:

lpProgressRoutine [in, optional] The address of a callback function of type LPPROGRESS_ROUTINE that is called each time another portion of the file has been copied. This parameter can be NULL. For more information on the progress callback function, see the CopyProgressRoutine function.

documentation of this ProgressRoutine声明,当开始复制或完成复制文件的垃圾时调用此回调。如果回调函数返回12(取消、停止),则可以取消复制过程。然而,当垃圾副本停止时,这个回调函数似乎没有被调用。

所以我的问题是:当它停止时,如何在每个文件的基础上取消此副本?

最佳答案

win32file.CopyFileEx不允许将 Cancel 作为 bool 值或整数值以外的任何值传递。在 API 中,它是一个 LPBOOL 指针,允许调用者在另一个线程中同时设置其值。您必须使用 ctypes、Cython 或 C 扩展才能获得这种级别的控制。下面我使用 ctypes 编写了一个示例。

如果取消复制不起作用,因为线程在同步 I/O 上被阻塞,您可以尝试调用 CancelIoEx在进度例程中传递的文件句柄上,或 CancelSynchronousIo取消线程的所有同步 I/O。这些 I/O 取消功能是在 Windows Vista 中添加的。它们在 Windows XP 中不可用,以防您仍然支持它。

import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

COPY_FILE_FAIL_IF_EXISTS = 0x0001
COPY_FILE_RESTARTABLE = 0x0002
COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x0004
COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x0008
COPY_FILE_COPY_SYMLINK = 0x0800
COPY_FILE_NO_BUFFERING = 0x1000

CALLBACK_CHUNK_FINISHED = 0
CALLBACK_STREAM_SWITCH = 1
PROGRESS_CONTINUE = 0
PROGRESS_CANCEL = 1
PROGRESS_STOP = 2
PROGRESS_QUIET = 3

ERROR_REQUEST_ABORTED = 0x04D3

if not hasattr(wintypes, 'LPBOOL'):
wintypes.LPBOOL = ctypes.POINTER(wintypes.BOOL)

def _check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args

LPPROGRESS_ROUTINE = ctypes.WINFUNCTYPE(
wintypes.DWORD, # _Retval_
wintypes.LARGE_INTEGER, # _In_ TotalFileSize
wintypes.LARGE_INTEGER, # _In_ TotalBytesTransferred
wintypes.LARGE_INTEGER, # _In_ StreamSize
wintypes.LARGE_INTEGER, # _In_ StreamBytesTransferred
wintypes.DWORD, # _In_ dwStreamNumber
wintypes.DWORD, # _In_ dwCallbackReason
wintypes.HANDLE, # _In_ hSourceFile
wintypes.HANDLE, # _In_ hDestinationFile
wintypes.LPVOID) # _In_opt_ lpData

kernel32.CopyFileExW.errcheck = _check_bool
kernel32.CopyFileExW.argtypes = (
wintypes.LPCWSTR, # _In_ lpExistingFileName
wintypes.LPCWSTR, # _In_ lpNewFileName
LPPROGRESS_ROUTINE, # _In_opt_ lpProgressRoutine
wintypes.LPVOID, # _In_opt_ lpData
wintypes.LPBOOL, # _In_opt_ pbCancel
wintypes.DWORD) # _In_ dwCopyFlags

@LPPROGRESS_ROUTINE
def debug_progress(tsize, ttrnsfr, stsize, sttrnsfr, stnum, reason,
hsrc, hdst, data):
print('ttrnsfr: %d, stnum: %d, stsize: %d, sttrnsfr: %d, reason: %d' %
(ttrnsfr, stnum, stsize, sttrnsfr, reason))
return PROGRESS_CONTINUE

def copy_file(src, dst, cancel=None, flags=0,
cbprogress=None, data=None):
if isinstance(cancel, int):
cancel = ctypes.byref(wintypes.BOOL(cancel))
elif cancel is not None:
cancel = ctypes.byref(cancel)
if cbprogress is None:
cbprogress = LPPROGRESS_ROUTINE()
kernel32.CopyFileExW(src, dst, cbprogress, data, cancel, flags)

示例

if __name__ == '__main__':
import os
import tempfile
import threading

src_fd, src = tempfile.mkstemp()
os.write(src_fd, os.urandom(16 * 2 ** 20))
os.close(src_fd)
dst = tempfile.mktemp()

cancel = wintypes.BOOL(False)
t = threading.Timer(0.001, type(cancel).value.__set__, (cancel, True))
t.start()
try:
copy_file(src, dst, cancel, cbprogress=debug_progress)
except OSError as e:
print(e)
assert e.winerror == ERROR_REQUEST_ABORTED
finally:
if os.path.exists(src):
os.remove(src)
if os.path.exists(dst):
os.remove(dst)

关于python - 在 Windows 上取消 python 中的停滞文件复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42203579/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com