用DeepSeek点亮智慧火花:RAR文件变身ZIP的高效攻略

rar文件转成zip格式

这几天为了参加一个AI比赛项目,因此在完成项目的过程中难免遇到一些格式转换的问题,既然参加AI比赛,而且国产AI神器DeepSeek的确功能很强,何不体验一下。于是在DeepSeek的帮忙下,这个格式转换工具很快搞定,而且界面很美。下面与大家分享一下。

运行环境

操作系统:mac OS

解释器:Python3.8 +

编译器:Pycharm 2024.1

以下是源码:

import os
import sys
import zipfile
import subprocess
import threading
import queue
import shutil
import uuid
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext

class RARConverterApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("高级文件转换工具 v2.0    作者:Bruce_liu  【镇赉融媒】")
        self.geometry("800x600")
        self.check_dependencies()
        self._setup_ui()
        self._setup_styles()
        self.processing = False
        self.queue = queue.Queue()
        self.after(100, self.process_queue)

    def _setup_styles(self):
        """配置界面样式"""
        self.style = ttk.Style()
        self.style.theme_use('clam')
        
        # 配置颜色主题
        self.style.configure('TButton', padding=6)
        self.style.configure('Primary.TButton', 
                           foreground='white', 
                           background='#0078d4',
                           font=('Helvetica', 10, 'bold'))
        
        self.style.configure('Warning.TButton', 
                           foreground='white', 
                           background='#ffb900')
        
        self.style.configure('Danger.TButton', 
                           foreground='white', 
                           background='#d13438')

    def _setup_ui(self):
        """初始化界面组件"""
        main_frame = ttk.Frame(self, padding=20)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 文件选择区域
        file_frame = ttk.LabelFrame(main_frame, text=" 文件选择 ")
        file_frame.pack(fill=tk.X, pady=5)

        self.rar_path = tk.StringVar()
        ttk.Entry(file_frame, textvariable=self.rar_path, width=50).pack(side=tk.LEFT, padx=5, expand=True)
        ttk.Button(file_frame, text="选择RAR文件", command=self.browse_rar, style='Primary.TButton').pack(side=tk.LEFT)

        # 输出设置
        output_frame = ttk.LabelFrame(main_frame, text=" 输出设置 ")
        output_frame.pack(fill=tk.X, pady=5)

        self.zip_path = tk.StringVar()
        ttk.Entry(output_frame, textvariable=self.zip_path, width=50).pack(side=tk.LEFT, padx=5, expand=True)
        ttk.Button(output_frame, text="保存位置", command=self.browse_zip, style='Primary.TButton').pack(side=tk.LEFT)

        # 进度显示
        self.progress = ttk.Progressbar(main_frame, orient=tk.HORIZONTAL, mode='determinate')
        self.progress.pack(fill=tk.X, pady=10)

        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text=" 操作日志 ")
        log_frame.pack(fill=tk.BOTH, expand=True)

        self.log_area = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, state=tk.DISABLED)
        self.log_area.pack(fill=tk.BOTH, expand=True)

        # 控制按钮
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(pady=10)

        self.start_btn = ttk.Button(btn_frame, text="开始转换", command=self.start_conversion, style='Primary.TButton')
        self.start_btn.pack(side=tk.LEFT, padx=5)

        self.cancel_btn = ttk.Button(btn_frame, text="取消转换", command=self.cancel_conversion, 
                                   style='Warning.TButton', state=tk.DISABLED)
        self.cancel_btn.pack(side=tk.LEFT, padx=5)

        ttk.Button(btn_frame, text="退出程序", command=self.safe_exit, style='Danger.TButton').pack(side=tk.RIGHT)

    def browse_rar(self):
        """选择RAR文件"""
        path = filedialog.askopenfilename(
            title="选择RAR文件",
            filetypes=[("RAR文件", "*.rar"), ("所有文件", "*.*")]
        )
        if path:
            self.rar_path.set(path)
            default_zip = os.path.splitext(path)[0] + "_converted.zip"
            self.zip_path.set(default_zip)

    def browse_zip(self):
        """选择ZIP保存路径"""
        path = filedialog.asksaveasfilename(
            title="保存ZIP文件",
            defaultextension=".zip",
            filetypes=[("ZIP文件", "*.zip"), ("所有文件", "*.*")]
        )
        if path:
            self.zip_path.set(path)

    def start_conversion(self):
        """启动转换流程"""
        if self.processing:
            return

        if not self._validate_inputs():
            return

        self.processing = True
        self._update_ui_state(starting=True)
        self.log("开始转换操作...", "blue")

        # 启动转换线程
        threading.Thread(target=self.conversion_process, daemon=True).start()

    def _validate_inputs(self):
        """验证输入有效性"""
        if not os.path.exists(self.rar_path.get()):
            messagebox.showerror("错误", "请选择有效的RAR文件")
            return False

        if not self.zip_path.get().endswith(".zip"):
            messagebox.showerror("错误", "输出文件必须是ZIP格式")
            return False

        return True

    def conversion_process(self):
        """执行转换的核心方法"""
        temp_dir = None
        try:
            # 生成唯一临时目录
            temp_dir = os.path.join(
                os.getcwd(),
                f"temp_{uuid.uuid4().hex[:8]}"  # 8位随机ID
            )
            os.makedirs(temp_dir, exist_ok=True)

            # 阶段1:解压RAR
            self.log("正在解压RAR文件...")
            result = subprocess.run(
                ["unar", "-o", temp_dir, self.rar_path.get()],
                capture_output=True,
                text=True
            )
            
            if result.returncode != 0:
                raise RuntimeError(f"解压失败:{result.stderr}")

            self.queue.put(("progress", 30))

            # 阶段2:创建ZIP
            self.log("正在创建ZIP文件...")
            with zipfile.ZipFile(
                self.zip_path.get(), 
                'w', 
                zipfile.ZIP_DEFLATED,
                compresslevel=6,
                allowZip64=True
            ) as zipf:
                zipf.encoding = 'utf-8'
                base_path = os.path.abspath(temp_dir)
                all_files = self._get_file_list(temp_dir)
                total = len(all_files)
                
                for idx, (full_path, relative_path) in enumerate(all_files, 1):
                    if not self.processing:
                        raise InterruptedError("用户取消操作")
                    
                    zipf.write(full_path, relative_path)
                    self.queue.put(("progress", 30 + int(70*(idx/total))))
                    self.queue.put(("log", f"添加文件:{relative_path}"))

            # 最终验证
            if self._verify_zip(temp_dir):
                self.queue.put(("success", "转换完成且通过验证"))
            else:
                raise RuntimeError("ZIP文件验证失败")

        except Exception as e:
            self.queue.put(("error", str(e)))
        finally:
            if temp_dir and os.path.exists(temp_dir):
                shutil.rmtree(temp_dir, ignore_errors=True)
            self.queue.put(("complete", None))

    def _get_file_list(self, root_dir):
        """生成要压缩的文件列表"""
        file_list = []
        base_path = os.path.abspath(root_dir)
        
        for root, _, files in os.walk(root_dir):
            # 跳过隐藏目录
            if os.path.basename(root).startswith('.'):
                continue
                
            for file in files:
                # 过滤系统文件
                if file.startswith(('.', '~$')) or file == 'Thumbs.db':
                    continue
                    
                full_path = os.path.join(root, file)
                relative_path = os.path.relpath(full_path, base_path)
                file_list.append((full_path, relative_path))
        
        return file_list

    def _verify_zip(self, original_dir):
        """验证ZIP文件完整性"""
        try:
            with zipfile.ZipFile(self.zip_path.get(), 'r') as zf:
                if zf.testzip() is not None:
                    return False
                
                # 检查文件数量是否一致
                orig_count = len(self._get_file_list(original_dir))
                zip_count = len(zf.infolist())
                return orig_count == zip_count
        except:
            return False

    def cancel_conversion(self):
        """取消转换操作"""
        if messagebox.askyesno("确认取消", "确定要中止当前转换操作吗?"):
            self.processing = False
            self.log("正在取消操作...", "orange")

    def safe_exit(self):
        """安全退出程序"""
        if self.processing:
            if messagebox.askyesno("退出确认", "转换正在进行中,确定要退出吗?"):
                self.processing = False
                self.destroy()
        else:
            self.destroy()

    def process_queue(self):
        """处理消息队列"""
        try:
            while True:
                msg_type, content = self.queue.get_nowait()
                
                if msg_type == "progress":
                    self.progress["value"] = content
                elif msg_type == "log":
                    self.log(content)
                elif msg_type == "success":
                    messagebox.showinfo("成功", content)
                    self.log(content, "green")
                elif msg_type == "error":
                    messagebox.showerror("错误", content)
                    self.log(content, "red")
                elif msg_type == "complete":
                    self._update_ui_state(starting=False)

        except queue.Empty:
            pass
        finally:
            self.after(100, self.process_queue)

    def _update_ui_state(self, starting=True):
        """更新界面状态"""
        state = tk.NORMAL if not starting else tk.DISABLED
        self.start_btn.config(state=state)
        self.cancel_btn.config(state=tk.NORMAL if starting else tk.DISABLED)
        self.progress["value"] = 0 if not starting else self.progress["value"]

    def log(self, message, color="black"):
        """记录日志信息"""
        color_tags = {
            "blue": ("blue", "#0078d4"),
            "green": ("green", "#107c10"),
            "red": ("red", "#d13438"),
            "orange": ("orange", "#ff8c00")
        }
        
        self.log_area.config(state=tk.NORMAL)
        
        # 配置标签
        for tag, (fg, bg) in color_tags.items():
            self.log_area.tag_config(tag, foreground=fg)
        
        self.log_area.insert(tk.END, message + "\n", color)
        self.log_area.see(tk.END)
        self.log_area.config(state=tk.DISABLED)

    def check_dependencies(self):
        """检查系统依赖"""
        try:
            subprocess.run(["unar", "--version"], 
                          check=True,
                          stdout=subprocess.DEVNULL,
                          stderr=subprocess.DEVNULL)
        except (FileNotFoundError, subprocess.CalledProcessError):
            messagebox.showerror(
                "缺少依赖",
                "需要安装unar工具\n\n"
                "安装方法:\n"
                "macOS: brew install unar\n"
                "Linux: sudo apt-get install unar\n"
                "Windows: 下载并安装7-Zip"
            )
            sys.exit(1)

if __name__ == "__main__":
    app = RARConverterApp()
    app.mainloop()

主要功能特点:

  1. 现代化界面设计
  • 彩色按钮状态指示(蓝色-运行中,黄色-可取消,红色-退出)
  • 实时进度条显示
  • 带颜色分级的日志系统
  • 自适应布局
  1. 增强稳定性
  • 完整的异常处理链
  • 临时文件自动清理
  • 操作取消确认
  • ZIP文件完整性验证
  1. 性能优化
  • 流式文件处理(避免内存溢出)
  • 多线程处理保持界面响应
  • 智能文件过滤(跳过隐藏文件)
  1. 易用性改进
  • 自动建议输出路径
  • 输入有效性验证
  • 友好的错误提示
  • 系统依赖自动检查

使用说明:

  1. 安装依赖

    # macOS
    brew install unar
    
    # Ubuntu/Debian
    sudo apt-get install unar
    
    # Windows
    # 安装7-Zip并添加至系统PATH
    
  2. 运行程序

    python converter_gui.py
    
  3. 操作流程

    1. 点击"选择RAR文件"按钮选择输入文件
    2. 自动生成默认输出路径(可手动修改)
    3. 点击"开始转换"启动操作
    4. 使用"取消转换"按钮可中止操作
    5. 通过日志区域查看实时进度

当遇到问题时,程序会显示颜色编码的错误信息:

  • 蓝色:操作提示
  • 绿色:成功信息
  • 红色:错误信息
  • 橙色:警告信息

运行结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bruce_xiaowei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值