PDF + ZIP 合并器:把ZIP文件打包至PDF文件中

[复制链接]
252 |10
发表于 2025-10-24 19:45:54 | 显示全部楼层 |阅读模式
因工作需要,需要将文件导入内网电脑,但一个办公区只能有一个公用U盘,操作不便,确可以通过手机App将文件传至内网(限制办公文件类型的传输),故开发此工具,KIMI提供帮助。
新文件会自动重命名,如new,new1,new2...



                  ICO图标       


    PYTHON直接运行,直接复制下面代码即可

[Python]  
  1. import os,sys
  2. import configparser
  3. import tkinter as tk
  4. from tkinter import filedialog, messagebox, ttk
  5. from tkinterdnd2 import DND_FILES, TkinterDnD
  6. import subprocess
  7. import platform
  8. from pathlib import Path
  9. # ---------- 持久化 ini 路径 ----------
  10. def get_ini_path() -> Path:
  11.     """
  12.     返回一个可持久写入的 ini 文件路径。
  13.     开发时放在脚本目录;打包后放在用户数据目录。
  14.     """
  15.     if getattr(sys, 'frozen', False):
  16.         # PyInstaller 打包后
  17.         base = Path.home()  / 'PdfZipMerger' \
  18.             if os.name == 'nt' else Path.home() / '.config' / 'PdfZipMerger'
  19.     else:
  20.         # 源码运行
  21.         base = Path(__file__).parent
  22.     base.mkdir(parents=True, exist_ok=True)
  23.     return base / 'zipInPdfSettings.ini'
  24. INI_FILE = get_ini_path()
  25. # ---------- 配置 ----------
  26. def load_output_dir() -> str:
  27.     cfg = configparser.ConfigParser()
  28.     cfg.read(INI_FILE, encoding="utf-8")
  29.     return cfg.get("DEFAULT", "output_dir", fallback="")
  30. def save_output_dir(path: str):
  31.     cfg = configparser.ConfigParser()
  32.     cfg["DEFAULT"] = {"output_dir": path}
  33.     with open(INI_FILE, "w", encoding="utf-8") as f:
  34.         cfg.write(f)
  35. # ---------- 文件名 ----------
  36. def auto_increment_filename(full_path: str) -> str:
  37.     if not os.path.exists(full_path):
  38.         return full_path
  39.     base, ext = os.path.splitext(full_path)
  40.     counter = 1
  41.     while True:
  42.         new_name = f"{base}{counter}{ext}"
  43.         if not os.path.exists(new_name):
  44.             return new_name
  45.         counter += 1
  46. # ---------- 拖拽 ----------
  47. def drop_pdf(event):
  48.     files = root.tk.splitlist(event.data)
  49.     if files:
  50.         pdf_var.set(files[0])
  51.         if not out_dir_var.get():
  52.             out_dir_var.set(os.path.dirname(files[0]))
  53. def drop_zip(event):
  54.     files = root.tk.splitlist(event.data)
  55.     if files:
  56.         zip_var.set(files[0])
  57. # ---------- 浏览 ----------
  58. def browse_pdf():
  59.     path = filedialog.askopenfilename(title="选择 PDF 文件", filetypes=[("PDF files", "*.pdf")])
  60.     if path:
  61.         pdf_var.set(path)
  62.         if not out_dir_var.get():
  63.             out_dir_var.set(os.path.dirname(path))
  64. def browse_zip():
  65.     path = filedialog.askopenfilename(title="选择 ZIP 文件", filetypes=[("ZIP files", "*.zip")])
  66.     if path:
  67.         zip_var.set(path)
  68. def browse_out_dir():
  69.     path = filedialog.askdirectory(title="选择输出目录")
  70.     if path:
  71.         out_dir_var.set(path)
  72.         save_output_dir(path)
  73. # ---------- 合并 ----------
  74. def merge():
  75.     pdf_path = pdf_var.get().strip('"')
  76.     zip_path = zip_var.get().strip('"')
  77.     out_dir = out_dir_var.get().strip()
  78.     base_name = out_name_var.get().strip() or "new"
  79.     if not pdf_path or not zip_path:
  80.         messagebox.showerror("错误", "请选择 PDF 和 ZIP 文件!")
  81.         return
  82.     if not out_dir:
  83.         messagebox.showerror("错误", "请选择输出目录!")
  84.         return
  85.     full_out = auto_increment_filename(os.path.join(out_dir, base_name + ".pdf"))
  86.     try:
  87.         with open(full_out, "wb") as out_file:
  88.             out_file.write(open(pdf_path, "rb").read())
  89.             out_file.write(open(zip_path, "rb").read())
  90.         messagebox.showinfo("成功", f"已合并为:{full_out}")
  91.     except Exception as e:
  92.         messagebox.showerror("合并失败", str(e))
  93. # ---------- 快捷按钮 ----------
  94. def open_out_dir():
  95.     out_dir = out_dir_var.get().strip()
  96.     print(out_dir)
  97.     # messagebox.showwarning("提示", f"输出目录不存在:{out_dir}")
  98.     if not out_dir:
  99.         messagebox.showwarning("提示", "请先选择输出目录!")
  100.         return
  101.     # 规范化路径,处理斜杠和反斜杠问题
  102.     out_dir = os.path.normpath(out_dir)
  103.     if not os.path.exists(out_dir):
  104.         messagebox.showerror("错误", f"输出目录不存在:{out_dir}")
  105.         return
  106.     try:
  107.         # 使用更健壮的方式打开文件浏览器
  108.         if platform.system() == "Windows":
  109.             print("Windows")
  110.             subprocess.run(["explorer", out_dir], check=True)
  111.             # os.startfile(out_dir)  # Windows系统使用os.startfile
  112.         elif platform.system() == "Darwin":
  113.             print("macOS")
  114.             subprocess.run(["open", out_dir], check=True)  # macOS使用open命令
  115.         else:  # Linux
  116.             print("Linux")
  117.             subprocess.run(["xdg-open", out_dir], check=True)  # Linux使用xdg-open
  118.     except Exception as e:
  119.         print("错误", f"无法打开文件夹:{str(e)}")
  120.         # messagebox.showerror("错误", f"无法打开文件夹:{str(e)}")
  121. def clear_inputs():
  122.     pdf_var.set("")
  123.     zip_var.set("")
  124. # ---------- GUI ----------
  125. root = TkinterDnD.Tk()
  126. root.title("PDF + ZIP 合并器")
  127. root.geometry("600x290")
  128. root.resizable(False, False)
  129. style = ttk.Style()
  130. style.theme_use("vista")
  131. print(style.theme_names())
  132. # 配置按钮样式
  133. style.configure("Red.TButton", background="red", foreground="red")
  134. style.configure("Green.TButton", background="green", foreground="green")
  135. # 拖拽提示
  136. drag_tip = ttk.Label(root, text="PDF 和 ZIP 文件可直接拖拽到对应输入框中",
  137.                      foreground="RoyalBlue", anchor="center", font=("Segoe UI", 12 ))
  138. drag_tip.grid(row=0, column=0, columnspan=3, pady=(6, 0), sticky="ew", padx=10)
  139. # PDF
  140. ttk.Label(root, text="PDF 文件:").grid(row=1, column=0, padx=10, pady=8, sticky="e")
  141. pdf_var = tk.StringVar()
  142. pdf_entry = ttk.Entry(root, textvariable=pdf_var, width=55)
  143. pdf_entry.grid(row=1, column=1, padx=5, pady=8)
  144. pdf_entry.drop_target_register(DND_FILES)
  145. pdf_entry.dnd_bind("", drop_pdf)
  146. ttk.Button(root, text="浏览…", command=browse_pdf).grid(row=1, column=2, padx=5, pady=8)
  147. # ZIP
  148. ttk.Label(root, text="ZIP 文件:").grid(row=2, column=0, padx=10, pady=8, sticky="e")
  149. zip_var = tk.StringVar()
  150. zip_entry = ttk.Entry(root, textvariable=zip_var, width=55)
  151. zip_entry.grid(row=2, column=1, padx=5, pady=8)
  152. zip_entry.drop_target_register(DND_FILES)
  153. zip_entry.dnd_bind("", drop_zip)
  154. ttk.Button(root, text="浏览…", command=browse_zip).grid(row=2, column=2, padx=5, pady=8)
  155. # 输出目录
  156. ttk.Label(root, text="输出目录:").grid(row=3, column=0, padx=10, pady=8, sticky="e")
  157. out_dir_var = tk.StringVar(value=load_output_dir())
  158. out_dir_entry = ttk.Entry(root, textvariable=out_dir_var, width=55)
  159. out_dir_entry.grid(row=3, column=1, padx=5, pady=8)
  160. ttk.Button(root, text="浏览…", command=browse_out_dir).grid(row=3, column=2, padx=5, pady=8)
  161. # 输出文件名
  162. ttk.Label(root, text="新文件名:").grid(row=4, column=0, padx=10, pady=8, sticky="e")
  163. out_name_var = tk.StringVar(value="new")
  164. out_name_entry = ttk.Entry(root, textvariable=out_name_var, width=55)
  165. out_name_entry.grid(row=4, column=1, padx=5, pady=8, sticky="w")
  166. ttk.Button(root, text="打开输出文件夹", command=open_out_dir).grid(row=4, column=2, padx=5, pady=8)
  167. # ttk.Button(btn_frame, text="打开输出文件夹", command=open_out_dir).pack(side="left", padx=10)
  168. # 按钮行
  169. btn_frame = ttk.Frame(root)
  170. btn_frame.grid(row=5, column=0, columnspan=3, pady=12)
  171. # 使用Red.TButton样式
  172. ttk.Button(btn_frame, text="清空数据", command=clear_inputs, style="Red.TButton").pack(side="left", padx=10)
  173. # 使用Green.TButton样式
  174. ttk.Button(btn_frame, text="合并", command=merge, style="Green.TButton").pack(side="left", padx=10)
  175. drag_tip2 = ttk.Label(root, text=f"配置文件路径:{INI_FILE}",
  176.                      foreground="RoyalBlue", font=("Segoe UI", 10))
  177. drag_tip2.grid(row=6, column=0, columnspan=3, pady=(6, 0), sticky="w", padx=10)
  178. root.mainloop()
复制代码



    使用 pyinstaller 打包成单文件时需要添加 hook-tkinterdnd2.py  ,与上面代码 run.py 在同级目录即可,cmd 打包命令如下

[Python]  
  1. pyinstaller -F -w -n="PDF + ZIP 合并器" --icon=a.ico run.py --additional-hooks-dir=.
复制代码


    hook-tkinterdnd2.py 代码如下

[Python]  
  1. from PyInstaller.utils.hooks import collect_data_files
  2. datas = collect_data_files('tkinterdnd2')
复制代码





成品链接
https://xcherry.lanzouu.com/iQpQD32a0rwf

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

发表于 2025-10-24 19:46:24 | 显示全部楼层
改后缀没用啊,文件检测可不是傻子,会检测文件头和文件末尾的格式对不对的。
我把压缩包改成视频文件都需要老老实实塞一集猫和老鼠进去。然后有人下了资源发现这猫和老鼠真好看。
回复

使用道具 举报

发表于 2025-10-24 19:47:14 | 显示全部楼层
事实上直接把文件放进 office 文档里就是了
office 文档 docx   xlsx   pptx 等,本身就是个zip压缩文件
使用7zip 打开office 文档,往里面扔文件即可
添加了文件后的office文件依旧能打开,只是会报个错,不影响使用
回复

使用道具 举报

发表于 2025-10-24 19:47:25 | 显示全部楼层
有点意思的,先收藏一波
回复

使用道具 举报

发表于 2025-10-24 19:48:24 | 显示全部楼层
这个平时真用不着 不过用时真找不到  收藏是必须的
回复

使用道具 举报

发表于 2025-10-24 19:48:34 | 显示全部楼层
改后缀名行不行呢?zip直接改pdf。
回复

使用道具 举报

发表于 2025-10-24 19:49:28 | 显示全部楼层
收藏,谢谢啦
回复

使用道具 举报

发表于 2025-10-24 19:50:13 | 显示全部楼层
要看zip直接改成zip吗?
回复

使用道具 举报

发表于 2025-10-24 19:51:04 | 显示全部楼层
直接改后缀试过,直接被屏蔽了,会检测文件内容的
回复

使用道具 举报

发表于 2025-10-24 19:51:54 | 显示全部楼层
直接有压缩软件打开就行,也可以改后缀
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表