Windows平台
from pathlib import Path
import platform
import subprocess
def copy_file(file_path: str) -> None:
system = platform.system()
if system != "Windows":
return
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
clear_cmd = 'Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::Clear()'
copy_cmd = f'Get-Item {file_path} | Set-Clipboard'
ps_command = f'{clear_cmd}; {copy_cmd}'
args = ['powershell', ps_command]
subprocess.Popen(
args=args,
startupinfo=startupinfo,
shell=False,
creationflags=subprocess.CREATE_NO_WINDOW
)
复制文件到剪切板是一个相当常见的操作。为了方便复用,特将此代码留与此处。
调用方法:
copy_files("文件路径1", "文件路径2", ...)
这一方法而虽然不需要调用第三方库,但是它是直接将文件数据放置到剪切板的。如果文件很大速度是相当慢的。为了实现和原生Windows手动复制时一样的效果,就要明白Windows复制文件到剪贴板的原理:复制时并不是直接把文件内容放入剪贴板,而是写入 "文件拖放 / 复制粘贴的元数据(CF_HDROP 格式)",只记录文件路径,粘贴时才触发实际的文件拷贝。
要实现这一功能,必须借助Windows的API。安装:pip install pywin32
import os
import ctypes
import win32clipboard
from pathlib import Path
from typing import Iterable, Union
# 定义Windows API所需的结构体(CF_HDROP格式依赖)
class DROPFILES(ctypes.Structure):
_fields_ = [
("pFiles", ctypes.c_uint),
("x", ctypes.c_long),
("y", ctypes.c_long),
("fNC", ctypes.c_int),
("fWide", ctypes.c_int),
]
def copy_files_native_windows(file_paths: Iterable[Union[str, Path]]) -> None:
"""
模拟Windows原生复制文件到剪贴板(仅存路径,秒级完成,粘贴时有系统进度条)
:param file_paths: 多个文件路径(可来自不同文件夹)
"""
# 1. 过滤有效文件,转为绝对路径(统一用\\分隔符)
valid_paths = []
for path in file_paths:
abs_path = Path(path).absolute()
if abs_path.exists() and abs_path.is_file():
# 转为Windows原生路径(\\),并追加终止符\0
valid_paths.append(str(abs_path).replace("/", "\\") + "\0")
else:
print(f"忽略无效文件:{abs_path}")
if not valid_paths:
print("无有效文件,清空剪贴板")
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.CloseClipboard()
return
# 2. 构造CF_HDROP格式的剪贴板数据(系统原生格式)
# 拼接所有路径,最后加一个额外的\0表示路径列表结束
paths_str = "".join(valid_paths) + "\0"
# 转为宽字符(Windows原生用UTF-16)
paths_wchar = paths_str.encode("utf-16le")
# 初始化DROPFILES结构体(关键:告诉系统这是文件列表)
df = DROPFILES()
df.pFiles = ctypes.sizeof(DROPFILES) # 路径数据的起始偏移
df.fWide = 1 # 表示使用宽字符(UTF-16)
# 拼接结构体和路径数据
buffer = ctypes.string_at(ctypes.pointer(df), ctypes.sizeof(df)) + paths_wchar
# 3. 写入剪贴板(模拟系统原生复制)
try:
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
# 写入CF_HDROP格式(系统识别为"文件复制")
win32clipboard.SetClipboardData(win32clipboard.CF_HDROP, buffer)
print(f"已复制 {len(valid_paths)} 个文件到剪贴板(原生格式),可粘贴(粘贴时显示系统进度条)")
except Exception as e:
raise RuntimeError(f"写入剪贴板失败:{e}")
finally:
win32clipboard.CloseClipboard()
# ---------------------- 用法示例 ----------------------
if __name__ == "__main__":
# 复制不同文件夹的文件(比如C盘、D盘的大文件)
try:
copy_files_native_windows([
"C:/LargeFile1.zip", # 大文件1
"D:/Docs/LargeFile2.iso", # 不同文件夹的大文件2
Path("E:/测试/带空格的文件.mp4") # 带空格的文件
])
except RuntimeError as e:
print(f"操作失败:{e}")
跨平台方案
import subprocess
import platform
from pathlib import Path
from typing import Iterable, Union
def clear_clipboard() -> None:
"""跨平台清空剪贴板"""
system = platform.system()
startupinfo = None
# Windows 隐藏窗口配置
if system == "Windows":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
try:
if system == "Windows":
# Windows 清空剪贴板
clear_cmd = 'Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::Clear()'
subprocess.Popen(
args=['powershell', clear_cmd],
startupinfo=startupinfo,
shell=False,
creationflags=subprocess.CREATE_NO_WINDOW,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
elif system == "Darwin": # macOS
# macOS 清空剪贴板(兼容文件/文本)
subprocess.Popen(
args=['osascript', '-e', 'set the clipboard to ""'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
elif system == "Linux":
# Linux 优先用 GNOME 的 gio,其次 X11 的 xclip,最后 Wayland 的 wl-copy
try:
# GNOME 原生
subprocess.Popen(['gio', 'set-clipboard', '--clear'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
try:
# X11 环境
subprocess.Popen(['xclip', '-selection', 'clipboard', '-clear'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
try:
# Wayland 环境
subprocess.Popen(['wl-copy', '--clear'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
raise RuntimeError("Linux 未找到剪贴板工具:请安装 gio/xclip/wl-clipboard")
print("剪贴板已清空")
except Exception as e:
raise RuntimeError(f"清空剪贴板失败:{e}")
def copy_files_to_clipboard(file_paths: Iterable[Union[str, Path]]) -> None:
"""
跨平台复制多个文件到剪贴板(先清空,再复制)
:param file_paths: 多个文件路径的可迭代对象
:return: None
"""
# 1. 过滤有效文件(绝对路径,处理空格)
valid_paths = []
for path in file_paths:
file_path = Path(path).absolute()
if file_path.exists() and file_path.is_file():
valid_paths.append(str(file_path))
else:
print(f"忽略无效文件:{file_path}")
# 2. 先清空剪贴板
clear_clipboard()
# 3. 无有效文件则返回
if not valid_paths:
print("无有效文件,跳过文件复制")
return
# 4. 按系统执行复制命令
system = platform.system()
startupinfo = None
if system == "Windows":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
# Windows:PowerShell 复制多文件
ps_cmd = f'Get-Item {" ".join(valid_paths)} | Set-Clipboard'
args = ['powershell', '-Command', ps_cmd]
elif system == "Darwin":
# macOS:AppleScript 复制多文件
# 构造 AppleScript 的文件列表:{"path1", "path2"}
apple_paths = ', '.join([f'"{p}"' for p in valid_paths])
osascript_cmd = f'set the clipboard to {{POSIX file "{apple_paths}"}}'
args = ['osascript', '-e', osascript_cmd]
elif system == "Linux":
# Linux:优先用 gio(GNOME),兼容 X11/Wayland
try:
# GNOME 原生(推荐)
args = ['gio', 'set-clipboard', '--files'] + valid_paths
subprocess.run(args, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
try:
# X11 环境(xclip):文件剪贴板格式为 application/x-kde-cutselection
args = ['xclip', '-selection', 'clipboard', '-t', 'application/x-kde-cutselection'] + valid_paths
subprocess.run(args, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
try:
# Wayland 环境(wl-copy)
args = ['wl-copy', '--type', 'application/x-kde-cutselection'] + valid_paths
subprocess.run(args, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
raise RuntimeError("Linux 未找到剪贴板工具:请安装 gio/xclip/wl-clipboard")
# Linux 无需重新赋值 args(已在分支内执行)
return
else:
raise RuntimeError(f"不支持的操作系统:{system}(仅支持 Windows/macOS/Linux)")
# 执行 Windows/macOS 命令
try:
subprocess.Popen(
args=args,
startupinfo=startupinfo,
shell=False,
creationflags=subprocess.CREATE_NO_WINDOW if system == "Windows" else 0,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print(f"成功复制 {len(valid_paths)} 个文件到剪贴板")
except Exception as e:
raise RuntimeError(f"复制文件失败:{e}")
# ---------------------- 用法示例 ----------------------
if __name__ == "__main__":
# 跨平台复制示例
try:
copy_files_to_clipboard([
"/Users/test/file.txt", # macOS/Linux 路径
"D:/test/带空格的文件.docx", # Windows 路径
Path("./无效文件.txt")
])
except RuntimeError as e:
print(f"操作失败:{e}")
注意该跨平台并没有经过测试。但Windows平台的方法是确切可行的。