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

相关推荐
小鱼儿亮亮5 小时前
SSE传输方式的MCP服务器创建流程
python·mcp
B站_计算机毕业设计之家5 小时前
python招聘数据 求职就业数据可视化平台 大数据毕业设计 BOSS直聘数据可视化分析系统 Flask框架 Echarts可视化 selenium爬虫技术✅
大数据·python·深度学习·考研·信息可视化·数据分析·flask
任子菲阳5 小时前
学Java第五十三天——IO综合练习(1)
java·开发语言·爬虫
子夜江寒5 小时前
Python 学习-Day9-pandas数据导入导出操作
python·学习·pandas
繁华似锦respect5 小时前
单例模式出现多个单例怎么确定初始化顺序?
java·开发语言·c++·单例模式·设计模式·哈希算法·散列表
码农很忙5 小时前
让复杂AI应用构建像搭积木:Spring AI Alibaba Graph深度指南与源码拆解
开发语言·人工智能·python
渡我白衣5 小时前
计算机组成原理(1):计算机发展历程
java·运维·开发语言·网络·c++·笔记·硬件架构
霸王大陆5 小时前
《零基础学 PHP:从入门到实战》模块十:从应用到精通——掌握PHP进阶技术与现代化开发实战-4
开发语言·php
脾气有点小暴5 小时前
JavaScript 数据存储方法全解析:从基础到进阶
开发语言·javascript·ecmascript