从samba服务器下载文件工具

从 Samba 服务器下载文件工具

启用samba

项目概述

这是一个用于从 Samba 共享服务器下载文件/文件夹的工具,支持批量下载、模块化配置和自定义本地目录结构。

项目结构

复制代码
get-samba-file/
├── config.yaml                    # 配置文件(YAML格式)
├── run.bat                        # Windows 启动脚本
├── scripts/
│   └── samba_downloader.py        # 主 Python 脚本
├── doc/
│   └── 从samba服务器下载文件工具.md  # 本文档
└── downloads/                     # 下载文件存放目录(自动创建)
    ├── module_name1/              # 按模块名组织的子目录
    ├── module_name2/
    └── ...

环境要求

系统要求

  • 操作系统:Windows 7/8/10/11
  • Python 版本:3.7 或更高版本

Python 依赖包

脚本需要安装以下 Python 包:

  • PyYAML - 用于解析 YAML 配置文件
  • pysmb - 用于连接 Samba 服务器

安装步骤

1. 安装 Python

如果尚未安装 Python,请从以下地址下载并安装:

安装时请勾选 "Add Python to PATH" 选项,将 Python 添加到系统环境变量。

2. 安装依赖包

打开命令提示符(cmd)或 PowerShell,运行以下命令:

bash 复制代码
python -m pip install PyYAML pysmb

或者如果您的系统中有 python3 命令:

bash 复制代码
python3 -m pip install PyYAML pysmb

3. 验证安装

运行以下命令验证 Python 和依赖包是否正确安装:

bash 复制代码
python --version
python -c "import yaml; import smb; print('依赖包安装成功')"

配置说明

config.yaml 文件结构

配置文件采用 YAML 格式,支持模块化配置。以下是完整的配置示例:

yaml 复制代码
# Samba 服务器模块模板(可选,用于复用配置)
module_templates:
  # 示例:定义一个可复用的模块模板
  graphic: &graphic
    name: graphic
    items:
      - path: 633/out/arm64/targets/graphic/graphic_2d/
        # local_name: graphic_2d    # 可选:自定义本地目录名

# 设备列表
devices:
  # 设备1:192.168.16.17
  - ip: 192.168.16.17
    share_name: shared_folder_17
    username: root
    password: 123456
    modules:
      - name: kh_edgecomputing_thing_accessmanager_meta
        items:
          - path: kh5.0.1/out/arm64/targets/kh_edgecomputing/kh_edgecomputing_thing_accessmanager_meta/libnanning_subway_handler.z.so
            # local_name: libnanning_subway_handler.z.so    # 可选

  # 设备2:192.168.16.40
  - ip: 192.168.16.40
    share_name: shared_folder_40
    username: root
    password: 123456
    modules:
      # 引用模板(推荐)
      - *graphic
      - *multimedia
      - *audio_framework
      # 或直接定义
      - name: custom_module
        items:
          - path: some/remote/path/
            local_name: custom_name

配置字段说明

设备级配置(devices[].*)
  • ip:Samba 服务器 IP 地址(必需)
  • share_name:共享目录名称(必需)
  • username:登录用户名(可选,默认为空)
  • password:登录密码(可选,默认为空)
模块级配置(devices[].modules[])
  • name:模块名称,将作为本地下载的根目录名(必需)
  • items:该模块下的文件/目录列表(必需)
项目级配置(devices[].modules[].items[])
  • path:远程 Samba 共享中的路径(必需)
  • local_name:自定义本地文件名/目录名(可选)
    • 如果不指定,将使用远程路径的最后一部分作为本地名称

模板复用

YAML 支持锚点(&)和引用(*)语法,可以定义可复用的模块模板:

yaml 复制代码
module_templates:
  camera: &camera
    name: camera
    items:
      - path: 633/out/arm64/targets/multimedia/camera_framework/

devices:
  - ip: 192.168.16.40
    share_name: shared_folder_40
    modules:
      - *camera  # 引用模板

使用方法

1. 配置 config.yaml

根据您的 Samba 服务器信息编辑 config.yaml 文件。

2. 运行脚本

双击 run.bat 文件,或在命令提示符中运行:

bash 复制代码
run.bat

3. 交互操作

脚本会显示可用设备列表,用户选择设备后进入功能菜单:

  1. 显示设备完整配置:查看当前设备的详细配置
  2. 检测IP连通性:测试与设备的网络连接
  3. 获取文件/文件夹:下载文件(支持单选或批量下载)
  4. 返回设备选择界面:切换到其他设备
  5. 退出脚本:结束程序

下载规则

  • 目录结构 :文件按模块名组织,存放在 downloads/模块名/
  • 文件命名
    • 如果设置了 local_name,使用自定义名称
    • 否则使用远程路径的最后一部分
  • 目录下载:递归下载整个目录结构
  • 记录文件 :每次下载会在目标目录生成 info.txt,记录下载信息

故障排除

常见问题

  1. ModuleNotFoundError: No module named 'yaml'

    • 解决:运行 python -m pip install PyYAML pysmb
  2. 连接失败

    • 检查 IP 地址是否正确
    • 确认 Samba 服务正在运行
    • 验证用户名和密码
  3. 权限不足

    • 检查 Samba 共享权限设置
    • 确认用户有读取权限
  4. 中文乱码

    • 脚本已配置 UTF-8 编码,如仍有问题请检查控制台编码

日志查看

下载记录保存在各模块目录的 info.txt 文件中,包含:

  • 下载时间
  • 远程 IP 和共享名
  • 远程路径
  • 本地保存路径
  • 使用用户名

高级配置

多设备支持

配置文件支持多个设备,脚本会显示设备列表供选择。

批量下载

在文件选择界面输入特殊数字:

  • 0:退出当前界面
  • 所有项目数量 + 1:全部下载

自定义本地名称

通过 local_name 字段自定义下载后的文件名/目录名。

版本信息

  • 当前版本:v1.0
  • 支持平台:Windows
  • Python 要求:>= 3.7

完整文件内容

为了便于快速部署,以下是完整的文件内容,您可以直接复制创建相应文件。

1. 创建主脚本文件:scripts/samba_downloader.py

首先创建 scripts 目录,然后创建 samba_downloader.py 文件:

python 复制代码
import subprocess
import os
import sys
import importlib.util
from datetime import datetime  # 新增:用于记录下载时间

# 延迟导入 yaml,先给出友好提示
try:
    import yaml  # 新增:用于解析yaml文件
except ImportError:
    print("❌ 缺少依赖:PyYAML")
    print("👉 请运行:python -m pip install PyYAML pysmb")
    sys.exit(1)


def check_environment():
    """检测运行环境与依赖,缺失时给出提示"""
    # Python 版本要求
    major, minor = sys.version_info[:2]
    min_major, min_minor = 3, 7
    if (major, minor) < (min_major, min_minor):
        print(f"❌ Python 版本过低,当前 {major}.{minor},请升级到 >= {min_major}.{min_minor}")
        sys.exit(1)

    # 必需依赖
    requirements = [
        ("yaml", "PyYAML"),
        ("smb.SMBConnection", "pysmb"),
    ]
    missing = []
    for mod, pip_name in requirements:
        if importlib.util.find_spec(mod) is None:
            missing.append(pip_name)

    if missing:
        miss_str = ", ".join(missing)
        pip_cmd = f"python -m pip install {' '.join(missing)}"
        print(f"❌ 缺少依赖:{miss_str}")
        print(f"👉 请运行安装命令:{pip_cmd}")
        sys.exit(1)
    else:
        print(f"✅ 环境检查通过:Python {major}.{minor},依赖齐全")


def read_device_config():
    """
    读取yaml格式的设备配置文件,返回设备列表(包含IP、模块、路径及凭据)
    期望文件:config.yaml
    """
    config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "config.yaml")

    if not os.path.exists(config_path):
        print("❌ 未找到配置文件,请在脚本同目录创建 'config.yaml',示例:")
        print("""
devices:
  - ip: 192.168.16.40
    share_name: shared_folder_40
    username: root
    password: 123456
    modules:
      - name: camera
        items:
          - path: 633/out/arm64/targets/multimedia/camera_framework/
            local_name: camera_framework   # 可选:自定义本地目录/文件名
          - path: 001/out/tr12/multimedia/audio_framework/libaudio_service.z.so
      - name: graphic
        items:
          - path: 633/out/arm64/targets/graphic/graphic_2d/
""")
        sys.exit(1)

    try:
        with open(config_path, "r", encoding="utf-8") as f:
            raw = yaml.safe_load(f) or {}
    except Exception as e:
        print(f"❌ 读取 'config.yaml' 失败:{e}")
        sys.exit(1)

    if "devices" not in raw or not isinstance(raw["devices"], list) or not raw["devices"]:
        print("❌ 配置错误:config.yaml 中未找到 devices 列表")
        sys.exit(1)

    devices = []
    for dev in raw["devices"]:
        ip = dev.get("ip")
        share_name = dev.get("share_name")
        modules = dev.get("modules", [])

        if not ip or not share_name:
            print("❌ 配置错误:每个设备必须包含 ip 与 share_name")
            sys.exit(1)

        if not isinstance(modules, list) or not modules:
            print(f"❌ 配置错误:设备 {ip} 未配置 modules 或 modules 为空")
            sys.exit(1)

        normalized_modules = []
        for mod in modules:
            mod_name = mod.get("name")
            items = mod.get("items", [])
            if not mod_name:
                print(f"❌ 配置错误:设备 {ip} 的某个模块缺少 name")
                sys.exit(1)
            if not isinstance(items, list) or not items:
                print(f"❌ 配置错误:设备 {ip} 的模块 {mod_name} 缺少 items 列表")
                sys.exit(1)

            normalized_items = []
            for item in items:
                path = item.get("path")
                if not path:
                    print(f"❌ 配置错误:设备 {ip} 的模块 {mod_name} 存在缺少 path 的条目")
                    sys.exit(1)
                normalized_items.append({
                    "path": path,
                    "local_name": item.get("local_name", "").strip() or None
                })

            normalized_modules.append({
                "name": mod_name,
                "items": normalized_items
            })

        # 扁平化目标列表,便于展示和选择
        targets = []
        for mod in normalized_modules:
            for item in mod["items"]:
                targets.append({
                    "module": mod["name"],
                    "path": item["path"],
                    "local_name": item.get("local_name")
                })

        devices.append({
            "ip": ip,
            "share_name": share_name,
            "modules": normalized_modules,
            "targets": targets,
            # 确保用户名/密码为字符串,避免解析为数字导致 pysmb 报错
            "username": str(dev.get("username", "") or ""),
            "password": str(dev.get("password", "") or "")
        })

    return devices


def ping_ip(ip):
    """ping指定IP,返回是否连通(Windows系统)"""
    try:
        result = subprocess.run(
            ["ping", "-n", "2", "-w", "1000", ip],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        return result.returncode == 0
    except Exception as e:
        print(f"⚠️ ping检测出错:{str(e)}")
        return False


def select_device(devices):
    """让用户选择要连接的设备(显示IP及关键参数)"""
    print("\n📋 可用设备列表:")
    for i, dev in enumerate(devices, 1):
        print("--------------------------------------")
        print(f"   {i}. IP:{dev['ip']}")
        # 按模块分行展示目标
        module_targets = {}
        for t in dev["targets"]:
            display_name = t.get("local_name") or os.path.basename(t["path"].rstrip('/\\')) or t["path"]
            module_targets.setdefault(t["module"], []).append(display_name)
        total_targets = sum(len(v) for v in module_targets.values())
        print(f"      共享目录:{dev['share_name']} | 目标项:{total_targets} 个")
        print("      目标预览:")
        for module, names in module_targets.items():
            print(f"        [{module}] {', '.join(names)}")
    print("--------------------------------------")

    while True:
        try:
            choice = input(f"\n请输入要连接的设备序号(1-{len(devices)}):")
            if not choice.isdigit():
                print("❌ 请输入数字!")
                continue
            index = int(choice) - 1
            if 0 <= index < len(devices):
                return devices[index]
            else:
                print(f"❌ 序号超出范围,请输入1到{len(devices)}之间的数字!")
        except Exception as e:
            print(f"⚠️ 选择出错:{str(e)},请重新输入")


def function_menu(selected_device, is_single_device=False):
    """功能菜单(显示设备完整参数及所有远程路径)"""
    ip = selected_device["ip"]
    print(f"\n✅ 已选择设备:{ip},进入功能菜单:") # 恢复这条打印,确保一致性
    while True:
        print("\n-------------------")
        print("1. 显示设备完整配置")
        print("2. 检测IP连通性")
        print("3. 获取文件/文件夹")

        if not is_single_device:
            print("4. 返回设备选择界面")
            last_option_num = 5
        else:
            last_option_num = 4 # 如果没有"返回设备选择界面",则"退出脚本"是第4个选项

        print(f"{last_option_num}. 退出脚本")
        print("-------------------")

        try:
            func_choice = input(f"请选择功能(1-{last_option_num}):")
            if not func_choice.isdigit():
                print("❌ 无效选择,请输入数字!")
                continue

            choice_int = int(func_choice)

            if choice_int == 1:
                print("\n设备完整配置:")
                print(f"   IP:{selected_device['ip']}")
                print(f"   共享目录:{selected_device['share_name']}")
                print("   远程文件路径列表:")
                for i, t in enumerate(selected_device['targets'], 1):
                    display_name = t.get("local_name") or os.path.basename(t["path"].rstrip('/\\')) or t["path"]
                    print(f"      路径{i}:[{t['module']}] {t['path']} -> 本地名: {display_name}")
                print(f"   用户名:{selected_device['username'] or '未设置'}")
                print(f"   密码:{'*' * len(selected_device['password']) if selected_device['password'] else '未设置'}")  # 密码脱敏
            elif choice_int == 2:
                is_alive = ping_ip(ip)
                status = "正常" if is_alive else "中断"
                print(f"\n{ip} 连通性检测:{status}")
            elif choice_int == 3:
                handle_file_retrieval(selected_device)
            elif choice_int == 4 and not is_single_device: # 多设备时的"返回设备选择界面"
                print("\n返回设备选择界面...")
                return # 返回到main函数中的设备选择循环
            elif choice_int == last_option_num: # 退出脚本 (单设备或多设备的退出选项)
                print("\n👋 退出脚本,再见!")
                sys.exit(0)
            else:
                print(f"❌ 无效选择,请输入1-{last_option_num}之间的数字!")
        except Exception as e:
            print(f"⚠️ 功能执行出错:{str(e)}")


def handle_file_retrieval(selected_device):
    """处理文件/文件夹的获取逻辑"""
    ip = selected_device["ip"]
    share_name = selected_device["share_name"]
    targets = selected_device["targets"]
    username = selected_device["username"]
    password = selected_device["password"]

    local_base_dir = "./downloads"
    os.makedirs(local_base_dir, exist_ok=True)  # 确保本地下载目录存在

    while True:
        print(f"\n📦 可获取的文件/文件夹列表(来自 {ip}/{share_name}):")
        for i, t in enumerate(targets, 1):
            display_name = t.get("local_name") or os.path.basename(t["path"].rstrip('/\\')) or t["path"]
            print(f"   {i}. [{t['module']}] {t['path']} -> 本地: {t['module']}/{display_name}")
        print(f"   {len(targets) + 1}. 全部获取")
        print("   0. 退出当前界面") # 新增退出选项

        try:
            choice = input(f"\n请输入要获取的序号(0-{len(targets) + 1}):")
            if not choice.isdigit():
                print("❌ 请输入数字!")
                continue
            index = int(choice) - 1

            if index == -1: # 用户选择了退出
                print("返回功能菜单...")
                break # 退出当前循环,返回上一级菜单
            elif 0 <= index < len(targets):  # 用户选择了特定文件/文件夹
                target = targets[index]
                print(f"开始获取:[{target['module']}] {target['path']}")
                download_samb_item(ip, share_name, target["path"], local_base_dir, username, password, target["module"], target.get("local_name"))
                # 操作完成后不退出,继续显示当前菜单
            elif index == len(targets):  # 用户选择了全部获取
                print("开始获取所有配置的文件/文件夹...")
                for target in targets:
                    download_samb_item(ip, share_name, target["path"], local_base_dir, username, password, target["module"], target.get("local_name"))
                print("所有文件/文件夹获取完成!")
                # 操作完成后不退出,继续显示当前菜单
            else:
                print(f"❌ 序号超出范围,请输入0到{len(targets) + 1}之间的数字!")
        except Exception as e:
            print(f"⚠️ 选择出错:{str(e)},请重新输入")


def download_samb_item(ip, share_name, remote_path, local_base_dir, username, password, module_name, local_name=None):
    """从Samba共享下载文件或文件夹 (使用pysmb库),支持模块根目录与自定义本地名"""
    try:
        from smb.SMBConnection import SMBConnection
        from smb.smb_structs import OperationFailure
    except ImportError:
        print("❌ 缺少 'pysmb' 库。请运行 'pip install pysmb' 进行安装。")
        return

    conn = None
    try:
        # 创建连接
        client_machine_name = "PythonClient"
        server_name = ip

        # 确保凭据为字符串,避免 int 类型引发 encode 错误
        conn = SMBConnection(str(username or ""), str(password or ""), client_machine_name, server_name)
        if not conn.connect(ip, 445):
            print(f"❌ 无法连接到 Samba 共享 {ip}:445。请检查IP、用户名、密码和网络连接。")
            return

        # 确定本地保存路径
        remote_path_cleaned = remote_path.replace('\\', '/')

        # 首先获取远程路径的属性,以确定是文件还是目录
        is_directory = False
        try:
            smb_remote_path = remote_path_cleaned
            file_attrs = conn.getAttributes(share_name, smb_remote_path)
            is_directory = file_attrs.isDirectory
        except OperationFailure as e:
            print(f"❌ 远程路径 '{remote_path}' 不存在或无法访问。错误: {e}")
            return

        module_dir = os.path.join(local_base_dir, module_name)
        if is_directory:
            # 如果是文件夹,本地路径是 downloads/<模块名>/<自定义名或远程目录名>
            target_dir_name = local_name or os.path.basename(remote_path_cleaned.rstrip('/'))
            local_path = os.path.join(module_dir, target_dir_name)
            print(f"从 '{ip}/{share_name}/{remote_path}' 下载文件夹到 '{local_path}\\'...")
            _download_samba_directory(conn, share_name, remote_path_cleaned, local_path)
            print(f"✅ 文件夹 '{remote_path}' 下载完成!")
            _write_download_info(ip, share_name, remote_path, local_path, username, module_name, is_directory=True)
        else:
            # 如果是文件,本地路径是 downloads/<模块名>/<自定义名或文件名>
            file_name = local_name or os.path.basename(remote_path_cleaned)
            local_path = os.path.join(module_dir, file_name)

            print(f"从 '{ip}/{share_name}/{remote_path}' 下载文件到 '{local_path}'...")
            os.makedirs(os.path.dirname(local_path), exist_ok=True)
            with open(local_path, 'wb') as local_file:
                conn.retrieveFile(share_name, smb_remote_path, local_file)
            print(f"✅ 文件 '{remote_path}' 下载完成!")
            _write_download_info(ip, share_name, remote_path, local_path, username, module_name, is_directory=False)

    except OperationFailure as e:
        # 捕获更具体的Samba操作失败,例如认证失败或权限不足
        if e.ntStatus == 0xC0000022: # STATUS_ACCESS_DENIED
            print(f"❌ Samba权限不足或认证失败:请检查config.yaml中的用户名和密码,以及Samba服务器上的用户权限。错误: {e}")
        elif "LOGON_FAILURE" in str(e):
            print(f"❌ Samba认证失败:用户名或密码错误。请检查config.yaml配置。错误: {e}")
        else:
            print(f"❌ Samba操作失败:{e},请检查远程路径是否存在或权限。")
    except Exception as e:
        print(f"⚠️ 获取文件/文件夹时发生未知错误:{str(e)}")
    finally:
        if conn:
            conn.close()


def _download_samba_directory(conn, share_name, remote_dir, local_dir):
    os.makedirs(local_dir, exist_ok=True)

    file_list = conn.listPath(share_name, remote_dir.replace(os.sep, '/'))
    for f in file_list:
        if f.filename in ['.', '..']:
            continue
        current_remote_path = f"{remote_dir.rstrip('/')}/{f.filename}"
        current_local_path = os.path.join(local_dir, f.filename)

        if f.isDirectory:
            _download_samba_directory(conn, share_name, current_remote_path, current_local_path)
        else:
            print(f"      下载文件: {current_remote_path} -> {current_local_path}")
            os.makedirs(os.path.dirname(current_local_path), exist_ok=True)
            with open(current_local_path, 'wb') as local_file:
                conn.retrieveFile(share_name, current_remote_path.replace(os.sep, '/'), local_file)


def _write_download_info(ip, share_name, remote_path, local_path, username, module_name, is_directory=False):
    """将下载信息写入文件/文件夹的同级目录"""
    if is_directory:
        info_dir = local_path
    else:
        info_dir = os.path.dirname(local_path)

    os.makedirs(info_dir, exist_ok=True)
    download_info_file = os.path.join(info_dir, "info.txt")

    # 使用追加模式,每次下载都添加信息
    with open(download_info_file, 'a', encoding='utf-8') as f:
        f.write(f"[下载信息 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]\n")
        f.write(f"远程 IP: {ip}\n")
        f.write(f"Samba 共享名: {share_name}\n")
        f.write(f"远程路径: {remote_path}\n")
        f.write(f"本地保存路径: {local_path}\n")
        f.write(f"模块: {module_name}\n")
        f.write(f"用户名: {username}\n")
        f.write("-" * 30 + "\n")


def main():
    print("===== 设备连接工具 =====")
    check_environment()
    # 读取设备配置(包含IP及关联参数)
    devices = read_device_config()

    while True:
        if len(devices) == 1:
            selected_device = devices[0]
            # 如果是单设备,跳过设备选择提示,但仍进行连通性检测
            print(f"\n正在检测 {selected_device['ip']} 连通性...")
            if ping_ip(selected_device['ip']):
                function_menu(selected_device, is_single_device=True)
                # 如果从function_menu返回(用户选择了返回设备选择界面),且只有一个设备,则退出
                print("👋 脚本退出,再见!")
                sys.exit(0)
            else:
                print(f"❌ {selected_device['ip']} 连接失败(ping不通),脚本退出。")
                sys.exit(1)
        else: # 多个设备,需要用户选择
            selected_device = select_device(devices)
            print(f"\n正在检测 {selected_device['ip']} 连通性...")
            if ping_ip(selected_device['ip']):
                function_menu(selected_device, is_single_device=False)
                # 如果从function_menu返回(用户选择了返回设备选择界面),则继续外层循环,让用户重新选择设备
            else:
                print(f"❌ {selected_device['ip']} 连接失败(ping不通),请重新选择")


if __name__ == "__main__":
    main()

2. 创建配置文件:config.yaml

创建项目根目录下的 config.yaml 文件:

yaml 复制代码
# 配置范例说明(新手友好):
# - module_templates: 复用的模块模板,锚点名以 & 开头,引用时用 * 锚点名
#   - name: 模块名(同时作为本地下载的顶层目录)
#   - items: 要下载的文件/目录列表
#     - path: 远程路径(相对 Samba 共享根)
#     - local_name: 可选,自定义下载到本地的文件/目录名(不填则用远程 basename)
# - devices: 设备列表
#   - ip: 设备 IP
#   - share_name: Samba 共享名
#   - username/password: 登录凭据
#   - modules: 此设备要下载的模块;可直接写模块定义,也可引用模板(如 *graphic)
module_templates:
  # 如下载目录,则 name 可设为目录父级;如下载文件,则 name 可设为文件父目录
  xwdvk_d2000_small: &xwdvk_d2000_small
    name: xwdvk_d2000_small
    items:
      - path: 0/litea/out/xwdvk_d2000_small/xwdvk_d2000_small/usr/lib/libavfilter.so
        local_name: libavfilter.so
      - path: 0/litea/out/xwdvk_d2000_small/xwdvk_d2000_small/bin/opdemo_demo
        local_name: opdemo_demo.bin
      - path: 0/litea/out/xwdvk_d2000_small/xwdvk_d2000_small/rootfs/lib/libc.so
        local_name: libc.so
  graphic: &graphic
    name: graphic
    items:
      - path: 633/out/arm64/targets/graphic/graphic_2d/
        # local_name: graphic_2d    # 可选:指定本地目录名
  multimedia: &multimedia
    name: multimedia
    items:
      - path: 633/out/arm64/targets/multimedia/camera_framework/
        # local_name: camera_framework
  audio_framework: &audio_framework
    name: audio_framework
    items:
      - path: 001/out/tr12/multimedia/audio_framework/libaudio_service.z.so
      - path: 001/out/tr12/multimedia/audio_framework/libaudio_policy_service.z.so
  kh_distributed_input: &kh_distributed_input
    name: distributedhardware
    items:
      - path: 001/out/arm64/targets/kh_distributed/kh_distributed_screen_input/
  kh_distributed_screen: &kh_distributed_screen
    name: distributedhardware
    items:
      - path: 001/out/arm64/targets/kh_distributed/kh_distributed_screen/
  633camera: &633camera
    name: 633camera
    items:
      - path: 633/out/arm64/targets/hdf/drivers_peripheral_camera/libusb_camera_pipeline_core.z.so
      - path: 633/out/arm64/targets/hdf/drivers_peripheral_camera/libcamera_daemon.so
      - path: 633/out/arm64/targets/multimedia/camera_framework/libcamera_service.z.so
      - path: 633/out/arm64/targets/graphic/graphic_2d/librender_service.z.so

devices:
  - ip: 192.168.16.17
    share_name: shared_folder_17
    username: root
    password: 123456
    modules:
      - name: kh_edgecomputing_thing_accessmanager_meta
        items:
          - path: kh5.0.1/out/arm64/targets/kh_edgecomputing/kh_edgecomputing_thing_accessmanager_meta/libnanning_subway_handler.z.so
            # local_name: 自定义本地文件名,可选
      - *kh_distributed_screen
      - *kh_distributed_input

  - ip: 192.168.16.40
    share_name: shared_folder_40
    username: root
    password: 123456
    modules:
      #- *graphic
      #- *multimedia
      #- *audio_framework
      - *633camera

3. 创建启动脚本:run.bat

创建项目根目录下的 run.bat 文件:

batch 复制代码
@echo off
chcp 65001 >nul 2>&1
set "PYTHONUTF8=1"
set "PYTHONIOENCODING=utf-8"

set "script_dir=%~dp0"
set "py_script=%script_dir%scripts\samba_downloader.py"

if not exist "%py_script%" (
    echo ERROR: samba_downloader.py not found in current folder.
    pause
    exit /b 1
)

where python >nul 2>&1
if %errorlevel% equ 0 (
    set "python_cmd=python"
) else (
    where python3 >nul 2>&1
    if %errorlevel% equ 0 (
        set "python_cmd=python3"
    ) else (
        echo ERROR: Python not found. Install Python and add to PATH.
        echo Download: https://www.python.org/downloads/
        pause
        exit /b 1
    )
)

echo Starting samba_downloader.py ...
echo ==============================
"%python_cmd%" "%py_script%"

echo ==============================
echo Script finished. Press any key to exit...
pause >nul

文件创建步骤

  1. 创建目录结构

    bash 复制代码
    mkdir scripts
    mkdir doc
  2. 创建文件

    • 将上述第一个代码块内容保存为 scripts/samba_downloader.py
    • 将上述第二个代码块内容保存为 config.yaml
    • 将上述第三个代码块内容保存为 run.bat
  3. 安装依赖

    bash 复制代码
    python -m pip install PyYAML pysmb
  4. 运行脚本

    双击 run.bat 启动工具

更新日志

  • v1.0:初始版本,支持基本下载功能和 YAML 配置
相关推荐
爬山算法4 小时前
Netty(7)如何实现基于Netty的TCP客户端和服务器?
java·服务器·tcp/ip
牛奶咖啡134 小时前
Linux系统故障排查思路实践教程(上)
linux·服务器·linux系统故障排查思路·linux的日志分类与分析·忘记linux用户密码问题解决·系统无法启动问题解决·linux文件系统只读问题解决
想唱rap4 小时前
Linux下进程的控制
linux·运维·服务器·c++·算法
一叶之秋14124 小时前
QT常用控件(一)
服务器·开发语言·qt
极地星光4 小时前
Asio网络编程入门:从零构建同步客户端与服务器
服务器·网络
Xの哲學4 小时前
Linux ALSA音频架构: 从内核驱动到应用开发的全面解析
linux·服务器·算法·架构·边缘计算
初心_20244 小时前
11. 嵌入式Linux防火墙nftables的使用
linux·运维·服务器
脏脏a6 小时前
【Linux】进程调度算法、进程切换、环境变量
linux·运维·服务器
暴风游侠8 小时前
linux知识点-内核参数相关
linux·运维·服务器·笔记