网站建设业务越做越累,鲜花网网站建设的目的,做群头像的网站在线制作,荆门做网站的公司从 Samba 服务器下载文件工具
启用samba
项目概述
这是一个用于从 Samba 共享服务器下载文件/文件夹的工具#xff0c;支持批量下载、模块化配置和自定义本地目录结构。
项目结构
get-samba-file/
├── config.yaml # 配置文件#xff08;YAML格式…从 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/11Python 版本3.7 或更高版本Python 依赖包脚本需要安装以下 Python 包PyYAML- 用于解析 YAML 配置文件pysmb- 用于连接 Samba 服务器安装步骤1. 安装 Python如果尚未安装 Python请从以下地址下载并安装下载地址https://www.python.org/downloads/推荐版本Python 3.11安装时请勾选 “Add Python to PATH” 选项将 Python 添加到系统环境变量。2. 安装依赖包打开命令提示符cmd或 PowerShell运行以下命令python -m pipinstallPyYAML pysmb或者如果您的系统中有python3命令python3 -m pipinstallPyYAML pysmb3. 验证安装运行以下命令验证 Python 和依赖包是否正确安装python --version python -cimport yaml; import smb; print(依赖包安装成功)配置说明config.yaml 文件结构配置文件采用 YAML 格式支持模块化配置。以下是完整的配置示例# Samba 服务器模块模板可选用于复用配置module_templates:# 示例定义一个可复用的模块模板graphic:graphicname:graphicitems:-path:633/out/arm64/targets/graphic/graphic_2d/# local_name: graphic_2d # 可选自定义本地目录名# 设备列表devices:# 设备1192.168.16.17-ip:192.168.16.17share_name:shared_folder_17username:rootpassword:123456modules:-name:kh_edgecomputing_thing_accessmanager_metaitems:-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 # 可选# 设备2192.168.16.40-ip:192.168.16.40share_name:shared_folder_40username:rootpassword:123456modules:# 引用模板推荐-*graphic-*multimedia-*audio_framework# 或直接定义-name:custom_moduleitems:-path:some/remote/path/local_name:custom_name配置字段说明设备级配置devices[].*ipSamba 服务器 IP 地址必需share_name共享目录名称必需username登录用户名可选默认为空password登录密码可选默认为空模块级配置devices[].modules[]name模块名称将作为本地下载的根目录名必需items该模块下的文件/目录列表必需项目级配置devices[].modules[].items[]path远程 Samba 共享中的路径必需local_name自定义本地文件名/目录名可选如果不指定将使用远程路径的最后一部分作为本地名称模板复用YAML 支持锚点和引用*语法可以定义可复用的模块模板module_templates:camera:cameraname:cameraitems:-path:633/out/arm64/targets/multimedia/camera_framework/devices:-ip:192.168.16.40share_name:shared_folder_40modules:-*camera# 引用模板使用方法1. 配置 config.yaml根据您的 Samba 服务器信息编辑config.yaml文件。2. 运行脚本双击run.bat文件或在命令提示符中运行run.bat3. 交互操作脚本会显示可用设备列表用户选择设备后进入功能菜单显示设备完整配置查看当前设备的详细配置检测IP连通性测试与设备的网络连接获取文件/文件夹下载文件支持单选或批量下载返回设备选择界面切换到其他设备退出脚本结束程序下载规则目录结构文件按模块名组织存放在downloads/模块名/下文件命名如果设置了local_name使用自定义名称否则使用远程路径的最后一部分目录下载递归下载整个目录结构记录文件每次下载会在目标目录生成info.txt记录下载信息故障排除常见问题ModuleNotFoundError: No module named ‘yaml’解决运行python -m pip install PyYAML pysmb连接失败检查 IP 地址是否正确确认 Samba 服务正在运行验证用户名和密码权限不足检查 Samba 共享权限设置确认用户有读取权限中文乱码脚本已配置 UTF-8 编码如仍有问题请检查控制台编码日志查看下载记录保存在各模块目录的info.txt文件中包含下载时间远程 IP 和共享名远程路径本地保存路径使用用户名高级配置多设备支持配置文件支持多个设备脚本会显示设备列表供选择。批量下载在文件选择界面输入特殊数字0退出当前界面所有项目数量 1全部下载自定义本地名称通过local_name字段自定义下载后的文件名/目录名。版本信息当前版本v1.0支持平台WindowsPython 要求 3.7完整文件内容为了便于快速部署以下是完整的文件内容您可以直接复制创建相应文件。1. 创建主脚本文件scripts/samba_downloader.py首先创建scripts目录然后创建samba_downloader.py文件importsubprocessimportosimportsysimportimportlib.utilfromdatetimeimportdatetime# 新增用于记录下载时间# 延迟导入 yaml先给出友好提示try:importyaml# 新增用于解析yaml文件exceptImportError:print(❌ 缺少依赖PyYAML)print( 请运行python -m pip install PyYAML pysmb)sys.exit(1)defcheck_environment():检测运行环境与依赖缺失时给出提示# Python 版本要求major,minorsys.version_info[:2]min_major,min_minor3,7if(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[]formod,pip_nameinrequirements:ifimportlib.util.find_spec(mod)isNone:missing.append(pip_name)ifmissing:miss_str, .join(missing)pip_cmdfpython -m pip install{ .join(missing)}print(f❌ 缺少依赖{miss_str})print(f 请运行安装命令{pip_cmd})sys.exit(1)else:print(f✅ 环境检查通过Python{major}.{minor}依赖齐全)defread_device_config(): 读取yaml格式的设备配置文件返回设备列表包含IP、模块、路径及凭据 期望文件config.yaml config_pathos.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),config.yaml)ifnotos.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:withopen(config_path,r,encodingutf-8)asf:rawyaml.safe_load(f)or{}exceptExceptionase:print(f❌ 读取 config.yaml 失败{e})sys.exit(1)ifdevicesnotinrawornotisinstance(raw[devices],list)ornotraw[devices]:print(❌ 配置错误config.yaml 中未找到 devices 列表)sys.exit(1)devices[]fordevinraw[devices]:ipdev.get(ip)share_namedev.get(share_name)modulesdev.get(modules,[])ifnotipornotshare_name:print(❌ 配置错误每个设备必须包含 ip 与 share_name)sys.exit(1)ifnotisinstance(modules,list)ornotmodules:print(f❌ 配置错误设备{ip}未配置 modules 或 modules 为空)sys.exit(1)normalized_modules[]formodinmodules:mod_namemod.get(name)itemsmod.get(items,[])ifnotmod_name:print(f❌ 配置错误设备{ip}的某个模块缺少 name)sys.exit(1)ifnotisinstance(items,list)ornotitems:print(f❌ 配置错误设备{ip}的模块{mod_name}缺少 items 列表)sys.exit(1)normalized_items[]foriteminitems:pathitem.get(path)ifnotpath:print(f❌ 配置错误设备{ip}的模块{mod_name}存在缺少 path 的条目)sys.exit(1)normalized_items.append({path:path,local_name:item.get(local_name,).strip()orNone})normalized_modules.append({name:mod_name,items:normalized_items})# 扁平化目标列表便于展示和选择targets[]formodinnormalized_modules:foriteminmod[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)})returndevicesdefping_ip(ip):ping指定IP返回是否连通Windows系统try:resultsubprocess.run([ping,-n,2,-w,1000,ip],stdoutsubprocess.PIPE,stderrsubprocess.PIPE,textTrue)returnresult.returncode0exceptExceptionase:print(f⚠️ ping检测出错{str(e)})returnFalsedefselect_device(devices):让用户选择要连接的设备显示IP及关键参数print(\n 可用设备列表)fori,devinenumerate(devices,1):print(--------------------------------------)print(f{i}. IP{dev[ip]})# 按模块分行展示目标module_targets{}fortindev[targets]:display_namet.get(local_name)oros.path.basename(t[path].rstrip(/\\))ort[path]module_targets.setdefault(t[module],[]).append(display_name)total_targetssum(len(v)forvinmodule_targets.values())print(f 共享目录{dev[share_name]}| 目标项{total_targets}个)print( 目标预览)formodule,namesinmodule_targets.items():print(f [{module}]{, .join(names)})print(--------------------------------------)whileTrue:try:choiceinput(f\n请输入要连接的设备序号1-{len(devices)})ifnotchoice.isdigit():print(❌ 请输入数字)continueindexint(choice)-1if0indexlen(devices):returndevices[index]else:print(f❌ 序号超出范围请输入1到{len(devices)}之间的数字)exceptExceptionase:print(f⚠️ 选择出错{str(e)}请重新输入)deffunction_menu(selected_device,is_single_deviceFalse):功能菜单显示设备完整参数及所有远程路径ipselected_device[ip]print(f\n✅ 已选择设备{ip}进入功能菜单)# 恢复这条打印确保一致性whileTrue:print(\n-------------------)print(1. 显示设备完整配置)print(2. 检测IP连通性)print(3. 获取文件/文件夹)ifnotis_single_device:print(4. 返回设备选择界面)last_option_num5else:last_option_num4# 如果没有返回设备选择界面则退出脚本是第4个选项print(f{last_option_num}. 退出脚本)print(-------------------)try:func_choiceinput(f请选择功能1-{last_option_num})ifnotfunc_choice.isdigit():print(❌ 无效选择请输入数字)continuechoice_intint(func_choice)ifchoice_int1:print(\n设备完整配置)print(f IP{selected_device[ip]})print(f 共享目录{selected_device[share_name]})print( 远程文件路径列表)fori,tinenumerate(selected_device[targets],1):display_namet.get(local_name)oros.path.basename(t[path].rstrip(/\\))ort[path]print(f 路径{i}[{t[module]}]{t[path]}- 本地名:{display_name})print(f 用户名{selected_device[username]or未设置})print(f 密码{**len(selected_device[password])ifselected_device[password]else未设置})# 密码脱敏elifchoice_int2:is_aliveping_ip(ip)status正常ifis_aliveelse中断print(f\n{ip}连通性检测{status})elifchoice_int3:handle_file_retrieval(selected_device)elifchoice_int4andnotis_single_device:# 多设备时的返回设备选择界面print(\n返回设备选择界面...)return# 返回到main函数中的设备选择循环elifchoice_intlast_option_num:# 退出脚本 (单设备或多设备的退出选项)print(\n 退出脚本再见)sys.exit(0)else:print(f❌ 无效选择请输入1-{last_option_num}之间的数字)exceptExceptionase:print(f⚠️ 功能执行出错{str(e)})defhandle_file_retrieval(selected_device):处理文件/文件夹的获取逻辑ipselected_device[ip]share_nameselected_device[share_name]targetsselected_device[targets]usernameselected_device[username]passwordselected_device[password]local_base_dir./downloadsos.makedirs(local_base_dir,exist_okTrue)# 确保本地下载目录存在whileTrue:print(f\n 可获取的文件/文件夹列表来自{ip}/{share_name})fori,tinenumerate(targets,1):display_namet.get(local_name)oros.path.basename(t[path].rstrip(/\\))ort[path]print(f{i}. [{t[module]}]{t[path]}- 本地:{t[module]}/{display_name})print(f{len(targets)1}. 全部获取)print( 0. 退出当前界面)# 新增退出选项try:choiceinput(f\n请输入要获取的序号0-{len(targets)1})ifnotchoice.isdigit():print(❌ 请输入数字)continueindexint(choice)-1ifindex-1:# 用户选择了退出print(返回功能菜单...)break# 退出当前循环返回上一级菜单elif0indexlen(targets):# 用户选择了特定文件/文件夹targettargets[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))# 操作完成后不退出继续显示当前菜单elifindexlen(targets):# 用户选择了全部获取print(开始获取所有配置的文件/文件夹...)fortargetintargets: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}之间的数字)exceptExceptionase:print(f⚠️ 选择出错{str(e)}请重新输入)defdownload_samb_item(ip,share_name,remote_path,local_base_dir,username,password,module_name,local_nameNone):从Samba共享下载文件或文件夹 (使用pysmb库)支持模块根目录与自定义本地名try:fromsmb.SMBConnectionimportSMBConnectionfromsmb.smb_structsimportOperationFailureexceptImportError:print(❌ 缺少 pysmb 库。请运行 pip install pysmb 进行安装。)returnconnNonetry:# 创建连接client_machine_namePythonClientserver_nameip# 确保凭据为字符串避免 int 类型引发 encode 错误connSMBConnection(str(usernameor),str(passwordor),client_machine_name,server_name)ifnotconn.connect(ip,445):print(f❌ 无法连接到 Samba 共享{ip}:445。请检查IP、用户名、密码和网络连接。)return# 确定本地保存路径remote_path_cleanedremote_path.replace(\\,/)# 首先获取远程路径的属性以确定是文件还是目录is_directoryFalsetry:smb_remote_pathremote_path_cleaned file_attrsconn.getAttributes(share_name,smb_remote_path)is_directoryfile_attrs.isDirectoryexceptOperationFailurease:print(f❌ 远程路径 {remote_path} 不存在或无法访问。错误:{e})returnmodule_diros.path.join(local_base_dir,module_name)ifis_directory:# 如果是文件夹本地路径是 downloads/模块名/自定义名或远程目录名target_dir_namelocal_nameoros.path.basename(remote_path_cleaned.rstrip(/))local_pathos.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_directoryTrue)else:# 如果是文件本地路径是 downloads/模块名/自定义名或文件名file_namelocal_nameoros.path.basename(remote_path_cleaned)local_pathos.path.join(module_dir,file_name)print(f从 {ip}/{share_name}/{remote_path} 下载文件到 {local_path}...)os.makedirs(os.path.dirname(local_path),exist_okTrue)withopen(local_path,wb)aslocal_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_directoryFalse)exceptOperationFailurease:# 捕获更具体的Samba操作失败例如认证失败或权限不足ife.ntStatus0xC0000022:# STATUS_ACCESS_DENIEDprint(f❌ Samba权限不足或认证失败请检查config.yaml中的用户名和密码以及Samba服务器上的用户权限。错误:{e})elifLOGON_FAILUREinstr(e):print(f❌ Samba认证失败用户名或密码错误。请检查config.yaml配置。错误:{e})else:print(f❌ Samba操作失败{e}请检查远程路径是否存在或权限。)exceptExceptionase:print(f⚠️ 获取文件/文件夹时发生未知错误{str(e)})finally:ifconn:conn.close()def_download_samba_directory(conn,share_name,remote_dir,local_dir):os.makedirs(local_dir,exist_okTrue)file_listconn.listPath(share_name,remote_dir.replace(os.sep,/))forfinfile_list:iff.filenamein[.,..]:continuecurrent_remote_pathf{remote_dir.rstrip(/)}/{f.filename}current_local_pathos.path.join(local_dir,f.filename)iff.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_okTrue)withopen(current_local_path,wb)aslocal_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_directoryFalse):将下载信息写入文件/文件夹的同级目录ifis_directory:info_dirlocal_pathelse:info_diros.path.dirname(local_path)os.makedirs(info_dir,exist_okTrue)download_info_fileos.path.join(info_dir,info.txt)# 使用追加模式每次下载都添加信息withopen(download_info_file,a,encodingutf-8)asf:f.write(f[下载信息 -{datetime.now().strftime(%Y-%m-%d %H:%M:%S)}]\n)f.write(f远程 IP:{ip}\n)f.write(fSamba 共享名:{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)defmain():print( 设备连接工具 )check_environment()# 读取设备配置包含IP及关联参数devicesread_device_config()whileTrue:iflen(devices)1:selected_devicedevices[0]# 如果是单设备跳过设备选择提示但仍进行连通性检测print(f\n正在检测{selected_device[ip]}连通性...)ifping_ip(selected_device[ip]):function_menu(selected_device,is_single_deviceTrue)# 如果从function_menu返回用户选择了返回设备选择界面且只有一个设备则退出print( 脚本退出再见)sys.exit(0)else:print(f❌{selected_device[ip]}连接失败ping不通脚本退出。)sys.exit(1)else:# 多个设备需要用户选择selected_deviceselect_device(devices)print(f\n正在检测{selected_device[ip]}连通性...)ifping_ip(selected_device[ip]):function_menu(selected_device,is_single_deviceFalse)# 如果从function_menu返回用户选择了返回设备选择界面则继续外层循环让用户重新选择设备else:print(f❌{selected_device[ip]}连接失败ping不通请重新选择)if__name____main__:main()2. 创建配置文件config.yaml创建项目根目录下的config.yaml文件# 配置范例说明新手友好# - module_templates: 复用的模块模板锚点名以 开头引用时用 * 锚点名# - name: 模块名同时作为本地下载的顶层目录# - items: 要下载的文件/目录列表# - path: 远程路径相对 Samba 共享根# - local_name: 可选自定义下载到本地的文件/目录名不填则用远程 basename# - devices: 设备列表# - ip: 设备 IP# - share_name: Samba 共享名# - username/password: 登录凭据# - modules: 此设备要下载的模块可直接写模块定义也可引用模板如 *graphicmodule_templates:# 如下载目录则 name 可设为目录父级如下载文件则 name 可设为文件父目录xwdvk_d2000_small:xwdvk_d2000_smallname:xwdvk_d2000_smallitems:-path:0/litea/out/xwdvk_d2000_small/xwdvk_d2000_small/usr/lib/libavfilter.solocal_name:libavfilter.so-path:0/litea/out/xwdvk_d2000_small/xwdvk_d2000_small/bin/opdemo_demolocal_name:opdemo_demo.bin-path:0/litea/out/xwdvk_d2000_small/xwdvk_d2000_small/rootfs/lib/libc.solocal_name:libc.sographic:graphicname:graphicitems:-path:633/out/arm64/targets/graphic/graphic_2d/# local_name: graphic_2d # 可选指定本地目录名multimedia:multimedianame:multimediaitems:-path:633/out/arm64/targets/multimedia/camera_framework/# local_name: camera_frameworkaudio_framework:audio_frameworkname:audio_frameworkitems:-path:001/out/tr12/multimedia/audio_framework/libaudio_service.z.so-path:001/out/tr12/multimedia/audio_framework/libaudio_policy_service.z.sokh_distributed_input:kh_distributed_inputname:distributedhardwareitems:-path:001/out/arm64/targets/kh_distributed/kh_distributed_screen_input/kh_distributed_screen:kh_distributed_screenname:distributedhardwareitems:-path:001/out/arm64/targets/kh_distributed/kh_distributed_screen/633camera:633cameraname:633cameraitems:-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.sodevices:-ip:192.168.16.17share_name:shared_folder_17username:rootpassword:123456modules:-name:kh_edgecomputing_thing_accessmanager_metaitems:-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.40share_name:shared_folder_40username:rootpassword:123456modules:#- *graphic#- *multimedia#- *audio_framework-*633camera3. 创建启动脚本run.bat创建项目根目录下的run.bat文件echo off chcp 65001 nul 21 set PYTHONUTF81 set PYTHONIOENCODINGutf-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 21 if %errorlevel% equ 0 ( set python_cmdpython ) else ( where python3 nul 21 if %errorlevel% equ 0 ( set python_cmdpython3 ) 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文件创建步骤创建目录结构mkdirscriptsmkdirdoc创建文件将上述第一个代码块内容保存为scripts/samba_downloader.py将上述第二个代码块内容保存为config.yaml将上述第三个代码块内容保存为run.bat安装依赖python -m pipinstallPyYAML pysmb运行脚本双击run.bat启动工具更新日志v1.0初始版本支持基本下载功能和 YAML 配置