华为云OBS批量下载工具

之前开发中遇到数据迁移的需求,但是华为OBS对象存储不支持批到导出某个通的文件,于是自己用python写了这个工具,通过GUI界面简化了OBS文件下载流程,特别适合需要批量操作的技术人员使用。


一、工具核心功能亮点

  1. 可视化操作界面​:使用Tkinter构建的直观GUI
  2. 多线程下载​:后台线程处理避免界面卡顿
  3. 实时进度监控​:下载速度、当前文件、进度条三重反馈
  4. 智能路径处理​:自动根据华为云通路径创建本地目录结构
  5. 安全凭证管理​:Secret Key以掩码形式显示

三、使用到的关键库

库名称作用版本要求
tkinterGUI界面构建Python标准库
obs华为云OBS官方SDKpip install obs
requestsHTTP请求处理pip install requests
pathlib路径操作Python标准库

四、使用示例

  1. 填写AK/SK凭证
  2. 设置区域端点(默认西南区)
  3. 输入桶名称
  4. 选择桶内文件夹路径
  5. 保存到本地目录路径
  6. 点击"获取并下载"启动任务


 

总结

通过Python的Tkinter与华为OBS SDK结合,实现了:

  • ✅ 图形化批量下载操作
  • ✅ 实时进度反馈
  • ✅ 智能路径处理
  • ✅ 安全凭证管理

欢迎在评论区交流使用体验和改进建议!对于需要处理华为云OBS文件的技术人员,这个工具能极大提升工作效率。

附上完整源码,可自行编译运行。  
  
如需直接获取可执行文件,欢迎私信联系获取.exe安装包。  


六、完整源码

import os
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import requests
import threading
import time
from pathlib import Path
from obs import ObsClient, Object


class ObsAutoDownloader:
    def __init__(self, master):
        self.master = master
        master.title("华为云OBS自动下载工具")
        master.geometry("900x700")

        # 初始化状态
        self.is_downloading = False
        self.obs_client = None
        self.file_list = []
        self.current_file = ""

        # 创建UI组件
        self.create_widgets()

    def create_widgets(self):
        """界面布局"""
        main_frame = ttk.Frame(self.master, padding=20)
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 配置区域
        config_frame = ttk.LabelFrame(main_frame, text="OBS配置")
        config_frame.pack(fill=tk.X, pady=5)

        # 凭证输入
        ttk.Label(config_frame, text="Access Key:").grid(row=0, column=0, sticky=tk.W)
        self.ak_entry = ttk.Entry(config_frame)
        self.ak_entry.grid(row=0, column=1, padx=5, pady=2)

        ttk.Label(config_frame, text="Secret Key:").grid(row=0, column=2, sticky=tk.W)
        self.sk_entry = ttk.Entry(config_frame, show="*")
        self.sk_entry.grid(row=0, column=3, padx=5, pady=2)

        # 区域和桶配置
        ttk.Label(config_frame, text="区域端点:").grid(row=1, column=0, sticky=tk.W)
        self.endpoint_entry = ttk.Entry(config_frame)
        self.endpoint_entry.grid(row=1, column=1, padx=5, pady=2)
        self.endpoint_entry.insert(0, "obs.cn-southwest-2.myhuaweicloud.com")

        ttk.Label(config_frame, text="桶名称:").grid(row=1, column=2, sticky=tk.W)
        self.bucket_entry = ttk.Entry(config_frame)
        self.bucket_entry.grid(row=1, column=3, padx=5, pady=2)

        ttk.Label(config_frame, text="文件夹路径:").grid(row=2, column=0, sticky=tk.W)
        self.prefix_entry = ttk.Entry(config_frame)
        self.prefix_entry.grid(row=2, column=1, columnspan=3, sticky=tk.EW, padx=5, pady=2)
        self.prefix_entry.insert(0, "")

        # 保存路径
        ttk.Label(config_frame, text="保存目录:").grid(row=3, column=0, sticky=tk.W)
        self.save_path = tk.StringVar()
        ttk.Entry(config_frame, textvariable=self.save_path, width=40).grid(row=3, column=1, padx=5)
        ttk.Button(config_frame, text="浏览", command=self.choose_directory).grid(row=3, column=2)

        # 进度区域
        progress_frame = ttk.LabelFrame(main_frame, text="进度")
        progress_frame.pack(fill=tk.X, pady=5)

        self.progress_bar = ttk.Progressbar(progress_frame, mode='determinate')
        self.progress_bar.pack(fill=tk.X, padx=10, pady=5)

        info_frame = ttk.Frame(progress_frame)
        info_frame.pack(fill=tk.X, padx=10)
        ttk.Label(info_frame, text="当前文件:").pack(side=tk.LEFT)
        self.current_file_label = ttk.Label(info_frame, text="等待开始...")
        self.current_file_label.pack(side=tk.LEFT, padx=10)

        ttk.Label(info_frame, text="速度:").pack(side=tk.LEFT)
        self.speed_label = ttk.Label(info_frame, text="0.00 KB/s")
        self.speed_label.pack(side=tk.LEFT, padx=10)

        # 控制按钮
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(pady=5)
        self.start_btn = ttk.Button(btn_frame, text="获取并下载", command=self.start_process)
        self.start_btn.pack(side=tk.LEFT, padx=5)
        self.stop_btn = ttk.Button(btn_frame, text="停止", state=tk.DISABLED, command=self.stop_process)
        self.stop_btn.pack(side=tk.LEFT, padx=5)

        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="操作日志")
        log_frame.pack(fill=tk.BOTH, expand=True)
        self.log_text = scrolledtext.ScrolledText(log_frame, height=12)
        self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

    def choose_directory(self):
        path = filedialog.askdirectory()
        if path:
            self.save_path.set(path)

    def log(self, message):
        self.log_text.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {message}\n")
        self.log_text.see(tk.END)

    def validate_config(self):
        """验证配置有效性"""
        required_fields = {
            "AK": self.ak_entry.get().strip(),
            "SK": self.sk_entry.get().strip(),
            "Endpoint": self.endpoint_entry.get().strip(),
            "Bucket": self.bucket_entry.get().strip(),
            "SavePath": self.save_path.get()
        }

        for field, value in required_fields.items():
            if not value:
                messagebox.showerror("错误", f"{field} 不能为空")
                return False

        if not os.path.exists(required_fields["SavePath"]):
            try:
                os.makedirs(required_fields["SavePath"])
            except:
                messagebox.showerror("错误", "无法创建保存目录")
                return False

        return True

    def start_process(self):
        """启动整个流程"""
        if not self.validate_config():
            return

        self.start_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)
        self.is_downloading = True

        threading.Thread(target=self.full_process, daemon=True).start()

    def full_process(self):
        """完整处理流程"""
        try:
            # 初始化OBS客户端
            self.obs_client = ObsClient(
                access_key_id=self.ak_entry.get().strip(),
                secret_access_key=self.sk_entry.get().strip(),
                server=self.endpoint_entry.get().strip()
            )

            # 获取文件列表
            self.log("正在获取文件列表...")
            self.file_list = self.list_obs_files()
            if not self.file_list:
                self.log("目标文件夹为空")
                return

            self.log(f"找到 {len(self.file_list)} 个文件")

            # 开始下载
            self.download_all_files()

        except Exception as e:
            self.log(f"操作失败: {str(e)}")
        finally:
            self.is_downloading = False
            self.master.after(0, lambda: self.stop_btn.config(state=tk.DISABLED))
            self.master.after(0, lambda: self.start_btn.config(state=tk.NORMAL))

    def list_obs_files(self):
        """获取OBS文件列表"""
        bucket = self.bucket_entry.get().strip()
        prefix = self.prefix_entry.get().strip().rstrip('/') + '/'
        file_list = []
        marker = None

        try:
            while True:
                resp = self.obs_client.listObjects(bucket, prefix=prefix, marker=marker)
                if resp.status < 300:
                    # 过滤文件夹标记对象
                    valid_files = [
                        obj.key for obj in resp.body.contents
                        if not (obj.key.endswith('/') and obj.size == 0)
                    ]
                    file_list.extend(valid_files)

                    if resp.body.is_truncated:
                        marker = resp.body.next_marker
                    else:
                        break
                else:
                    raise Exception(f"列表获取失败: {resp.errorCode}")

            return file_list

        except Exception as e:
            self.log(f"获取文件列表失败: {str(e)}")
            return []

    def download_all_files(self):
        """下载所有文件"""
        total = len(self.file_list)
        success_count = 0
        start_time = time.time()

        for idx, file_key in enumerate(self.file_list, 1):
            if not self.is_downloading:
                break

            self.current_file = file_key
            self.master.after(0, lambda: self.current_file_label.config(
                text=f"{idx}/{total} {os.path.basename(file_key)}"))

            try:
                # 下载文件
                local_path = os.path.join(self.save_path.get(), file_key)
                os.makedirs(os.path.dirname(local_path), exist_ok=True)

                resp = self.obs_client.getObject(
                    self.bucket_entry.get().strip(),
                    file_key,
                    downloadPath=local_path
                )

                if resp.status < 300:
                    success_count += 1
                    self.log(f"下载成功: {file_key}")
                else:
                    self.log(f"下载失败[{resp.status}]: {file_key}")

                # 更新进度
                progress = (idx / total) * 100
                self.master.after(0, lambda: self.progress_bar.configure(value=progress))

                # 计算速度
                time_used = time.time() - start_time
                speed = (idx * 1024) / time_used  # 假设平均每个文件1KB
                self.master.after(0, lambda: self.speed_label.config(text=f"{speed:.2f} 文件/秒"))

            except Exception as e:
                self.log(f"下载异常 {file_key}: {str(e)}")

        self.log(f"下载完成!成功 {success_count}/{total} 个文件")

    def stop_process(self):
        """停止处理"""
        self.is_downloading = False
        self.log("操作已停止")
        if self.obs_client:
            self.obs_client.close()


if __name__ == "__main__":
    root = tk.Tk()
    app = ObsAutoDownloader(root)
    root.mainloop()

 

zhuaxia 可以从 xiami.com 和 music.163.com 下载 MP3 文件和音频。zhuaxia 是一个基于命令行的虾米音乐 ( www.xiami.com 以下简称[虾])和网易云音乐( music.163.com 以下简称[易]) 多线程批量下载工具zhuaxia 的开发调试环境:python 2.7.6依赖requests modulemutagen modulebeautifulsoup4 module特性自动识别解析URL. 目前支持:[虾] 歌曲,专辑,精选集,用户收藏[todo], 歌手热门[易] 歌曲,专辑,歌单,歌手热门下载歌手热门歌曲:数量可配置( 海外IP下载xiami资源" 一节加入实验性-p选项,尝试解决频繁请求被服务器ban的问题中英文命令行界面. 配置项 lang=en|cn 默认中文(cn)InstallationArchlinux 用户, zhuaxia可以从AUR中获取稳定版本(master branch):稳定版本:yaourt -S zhuaxia最新git版本(bleeding branch):yaourt -S zhuaxia-git其他用户:sudo python setup.py installUsage配置文件, 第一次运行zx后, 在$HOME/.zhuaxia/ 会有配置文件 zhuaxia.conf 配置参数有中文说明使用:zhuaxia (抓虾) -- 抓取[虾米音乐]和[网易云音乐]的 mp3 音乐 [CONFIG FILE:]   $HOME/.zhuaxia/zhuaxia.conf [OPTIONS]      -H : 首选HQ质量(320kbps),          > 虾米音乐  网易音乐 <             -无需特殊要求,直接下载高音质资源     -p : (实验性选项)使用代理池下载         在下载/解析量大的情况下,目标服务器会对禁止频繁的请求,所以zhuaxia可以自动获取         代理来解析和下载资源。因为获取的代理速度/可靠性不一,下载可能会缓慢或不稳定。     -h :显示帮助     -f :从文件下载     -v :显示版本信息 [USAGE]      zx [OPTION]          : 下载指定URL资源, 抓虾自动识别链接, 支持             - [虾] 歌曲,专辑,精选集,用户收藏,艺人TopN             - [易] 歌曲,专辑,歌单,艺人TopN         例子:            zx "http://www.xiami.com/space/lib-song/u/25531126"           zx "http://music.163.com/song?id=27552647"     zx [OPTION] -f           : 多个URL在一个文件中,每个URL一行。 URLs可以是混合[虾]和[易]的不同类型音乐资源。例子:           $ cat /tmp/foo.txt             http://music.163.com/artist?id=5345             http://www.xiami.com/song/1772130322             http://music.163.com/album?id=2635059             http://www.xiami.com/album/32449           $ zx -f /tmp/foo.txtProxy setting海外IP下载xiami资源xiami.com屏蔽了海外ip的http请求。在配置文件中添加(如果不存在的话)xiami.proxy.http=ip:port 可以让zhuaxia通过代理来解析xiami资源。 例如:xiami.proxy.http=127.0.0.1:8080这里ip:port构成的http代理是国内的代理服务器。 如果你的机器已经是国内的ip,请注释或删除这个选项。获取国内代理的简单方法:到http://proxy-list.org/ 搜索China的代理就好。Screenshotsdownloading (gif animation) parse input file parse url 标签:zhuaxia
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值