Python中剪贴板文件的自动元数据清理
一个Windows后台守护进程在复制后立即拦截剪贴板中的文件,并在粘贴前移除其元数据。这消除了使用ExifTool或系统工具手动处理图像、文档和视频的需求。处理过程在本地进行,无需外部数据传输,并具备防止损坏和符号链接攻击的保护机制。
通过Win32 API操作剪贴板
剪贴板监控由一个每500毫秒轮询剪贴板的ClipboardMonitor线程实现。关键格式是CF_HDROP,它包含文件路径列表。
pywin32的问题:
- 在旧版本中,
GetClipboardData(CF_HDROP)返回指向DROPFILES的指针,需要通过ctypes从shell32.dll调用DragQueryFileW。 - 在新版本中,它返回字符串列表,但PyInstaller或DLL不匹配可能破坏此行为。
检查代码:
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_HDROP):
file_list = win32clipboard.GetClipboardData(win32clipboard.CF_HDROP)
路径验证以确保安全
处理前,路径在validate_path中经过严格检查:
- 绝对路径。
- 比较原始inode和resolved_path(以阻止符号链接)。
- 排除
WINDIR中的目录。 - 限制文件数量(防止批量处理)。
这防止了符号链接攻击和处理系统目录。此外,检查resolved_path.is_file()以排除文件夹。
处理架构:原地覆盖
文件在其原始路径被覆盖,而不在剪贴板中替换——这简化了处理Windows内存锁。流程:
- 将文件复制到剪贴板。
- 检测
CF_HDROP。 - 验证并传递给
MetadataScrubber。 - 用清理后的版本覆盖。
图像清理:原子替换
支持JPEG、PNG、TIFF、BMP。移除EXIF数据,包括地理位置、设备和软件信息。
策略——使用临时文件确保原子性:
with Image.open(file_path) as img:
data = list(img.getdata())
image_without_exif = Image.new(img.mode, img.size)
image_without_exif.putdata(data)
temp_path = file_path.with_suffix(file_path.suffix + '.temp')
image_without_exif.save(temp_path, format=format_name)
shutil.move(str(temp_path), str(file_path))
确保完整性:粘贴时接收原始文件或清理后的文件。
DOCX:直接XML编辑
python-docx更改属性但不处理修订和批注。解决方案——解析_blob部分:
xml = part._blob.decode('utf-8')
if 'w:revision' in xml or 'w:ins' in xml or 'w:del' in xml:
xml = xml.replace('w:revision', 'w:revision_removed')\
.replace('w:ins ', 'w:p ')\
.replace('w:del ', 'w:p ')
part._blob = xml.encode('utf-8')
使用replace技巧移除根级别的修订标签,使文档在Word中变为最终版本。风险——复杂表格。
PDF和视频:基础支持
通过pypdf处理PDF:
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
writer.add_metadata({})
重建时不带元数据。视频(MP4、MKV、MOV)——基础原子清理;完全清理需要ffmpeg。
系统托盘问题
避免因Shell_NotifyIcon错误导致的通知:
- 不正确的HWND生命周期导致
DestroyWindow失败。 - COM对象在关闭Tkinter时崩溃。
解决方案——移除模块以确保后台稳定性。空闲时每500毫秒轮询导致0% CPU使用率。
关键要点
- 复制到剪贴板时自动清理元数据(EXIF、修订、地理位置)。
- 通过验证防止符号链接攻击和系统路径。
- 原子文件覆盖,无损坏风险。
- 本地Python处理(PIL、pywin32、pypdf),无需外部服务器。
- 优化的剪贴板监控,开销最小。
— Editorial Team
暂无评论。