Python复制文件到剪切板

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平台的方法是确切可行的。

相关推荐
笨拙的老猴子几秒前
[特殊字符] Java GC机制详解:G1、ZGC、Shenandoah全面解析与版本演进对比
java·开发语言
bellus-2 分钟前
ubuntu26测试win10的ollama大模型性能
python
水木流年追梦3 分钟前
大模型入门-Reward 奖励模型训练
开发语言·python·算法·leetcode·正则表达式
JavaWeb学起来3 分钟前
Python学习教程(六)数据结构List(列表)
数据结构·python·python基础·python教程
liuyunshengsir16 分钟前
PyTorch 动态量化(Dynamic Quantization)
人工智能·pytorch·python
电子云与长程纠缠25 分钟前
UE5制作六边形包裹球体效果
开发语言·python·ue5
砍材农夫31 分钟前
物联网 基于netty构建mqtt协议规范(遗嘱与保留消息)
java·开发语言·物联网·netty
DFT计算杂谈34 分钟前
KPROJ编译教程
java·前端·python·算法·conda
froginwe111 小时前
Python3 迭代器与生成器
开发语言
xiaoshuaishuai81 小时前
C# 签名异常与Gas预估失败调试方案
开发语言·网络·tcp/ip·c#