SMB(Server Message Block)协议实现对远程 Windows 共享服务器或 Samba 服务的文件读取

news/2025/11/7 14:22:06/文章来源:https://www.cnblogs.com/blbl-blog/p/19199555

1. 概述

💡 作者:古渡蓝按

个人微信公众号:微信公众号(深入浅出谈java)
感觉本篇对你有帮助可以关注一下,会不定期更新知识和面试资料、技巧!!!


本技术文档旨在说明如何通过 SMB(Server Message Block)协议 实现对远程 Windows 共享服务器或 Samba 服务的文件读取、写入与目录遍历操作。适用于 Java 应用程序在企业内网环境中安全、高效地访问远程共享资源。

主要应用场景包括:

  • 自动化从 Jenkins 构建服务器拉取构建产物;
  • 定期同步业务系统生成的配置/数据文件;
  • 批量处理远程共享目录中的特定类型文件(如 .hex.csv 等)。

1.1技术选型

组件 说明
协议 SMBv2 / SMBv3(推荐,安全性更高)
Java 库 jcifs-ng(JCIFS 的活跃维护分支,支持现代 SMB 协议)
认证方式 NTLM(Windows 域或本地账户)
开发语言 Java 8+

1.2前提条件

✅ 前提条件(必须满足)

在目标服务器 173.16.1.152 上:

  1. 已共享 D:\jenkins 文件夹(这里改成你需要访问的共享目录
    • 共享名建议为 jenkins → 访问路径:\\173.16.1.152\jenkins目录名称改成自己相应即可
  2. 你有一个有写权限的 Windows 账户(如 admin / deploy
  3. 防火墙允许 445 端口(默认 SMB 端口)
  4. “密码保护的共享”已关闭(或你知道正确凭据)

💡 测试:在 winds 服务器上按 Win+R,输入
\\173.16.1.152\jenkins
看是否能打开并写入文件。


2、代码实现

代码执行流程示意图:

deepseek_mermaid_20251107_b9bbed


2.1、添加依赖

<dependency><groupId>eu.agno3.jcifs</groupId><artifactId>jcifs-ng</artifactId><version>2.1.9</version> <!-- 请使用最新稳定版 -->
</dependency>

2.2 提供接口核心代码

这部分主要是提供接口,和有些参数校验

@ApiOperation("只下载目录下的 .hex 文件并下载")@PostMapping("/getJenkinsHexData")public R<String> downloadSmbHexFiles(@RequestBody SmbDownloadRequestVo request) {// 1. 路径安全检查(防止路径遍历)if (request.getLocalBaseDir() != null &&(request.getLocalBaseDir().contains("..") ||request.getLocalBaseDir().contains("/"))) {throw new UserException("无效的本地基础目录路径");}//        // 2. 从环境变量获取密码(生产环境必须)
//        String safePassword = System.getenv("SMB_PASSWORD");
//        if (safePassword == null) {
//            throw new UserException("未设置SMB_PASSWORD环境变量");
//        }// 3. 验证请求参数if (request.getSmbHost() == null || request.getShareName() == null ||request.getUsername() == null) {throw new UserException("缺少必需参数:smbHost、shareName、username");}try {// 4. 使用安全密码执行下载WindowsDownloaderHexFile.downloadHexFiles(request.getSmbHost(),request.getShareName(),request.getRemotePath(),request.getUsername(),request.getPassword(),request.getLocalBaseDir(),true,request.getFileExtension());return R.ok("文件下载成功");} catch (Exception e) {return R.fail("文件下载失败");}}

2.3逻辑实现核心代码

具体代码


@Service
@Slf4j
public class WindowsDownloaderHexFile {/*** 从指定的 SMB 远程路径递归查找并下载所有 .hex 文件到本地目录。** @param smbHost           SMB 服务器地址 (e.g., "172.16.1.85")* @param shareName         SMB 共享名 (e.g., "jenkins")* @param remoteBasePath    需要开始搜索的远程基础路径 (相对于共享根目录)。支持多级,使用 \ 或 / 分隔。 (e.g., "1/8-位号文件/图号导入文件")*                          如果路径是目录,建议以分隔符结尾或确保它是目录。* @param username          用户名 (e.g., "administrator")* @param password          密码 (e.g., "Jn300880")* @param localDownloadDir  本地下载目录,找到的 .hex 文件将被下载到这里,并保持相对结构。(e.g., "D:\\DownloadedHexFiles")* @param preserveStructure 是否在本地保持远程的目录结构。true: 保持结构;false: 所有文件下载到 localDownloadDir 根目录下。* @param fileType   文件类型,指定只下载以该后缀的文件。* @throws RuntimeException 如果发生 IO、SMB 或其他错误*/public static void downloadHexFiles(String smbHost,String shareName,String remoteBasePath,String username,String password,String localDownloadDir,boolean preserveStructure,String fileType) {CIFSContext context = null;try {// 1. 初始化 SMB 上下文和认证context = SingletonContext.getInstance().withCredentials(new NtlmPasswordAuthenticator(null, username, password));// 2. 构建基础 SMB URLString baseSmbUrl = "smb://" + smbHost + "/" + shareName + "/";// 3. 处理 remoteBasePath,确保格式正确并构建目标 SmbFile// 移除路径开头和结尾的多余分隔符remoteBasePath = remoteBasePath.replaceAll("^[\\\\/]+|[\\\\/]+$", "");String targetSmbUrl = baseSmbUrl + (remoteBasePath.isEmpty() ? "" : remoteBasePath.replace("\\", "/") + "/");SmbFile targetRemoteDir = new SmbFile(targetSmbUrl, context);// 4. 检查远程基础路径是否存在且为目录if (!targetRemoteDir.exists()) {throw new RuntimeException("远程路径不存在: " + targetSmbUrl);}if (!targetRemoteDir.isDirectory()) {throw new RuntimeException("远程路径不是目录: " + targetSmbUrl);}Path localBasePath = Paths.get(localDownloadDir);System.out.println("尝试创建目录: " + localBasePath.toAbsolutePath());try {Files.createDirectories(localBasePath);System.out.println("目录创建成功");} catch (Exception e) {e.printStackTrace();}// 6. 开始递归查找和下载findAndDownloadHexFiles(targetRemoteDir, localBasePath, context, preserveStructure, targetRemoteDir.getCanonicalPath(),fileType);} catch (Exception e) {throw new RuntimeException("初始化 SMB 连接或准备下载时出错", e);}}/*** 递归查找 .hex 文件并下载的核心方法。** @param currentRemoteDir 当前正在处理的远程目录 SmbFile。* @param localBasePath    本地下载的基础目录 Path。* @param context          SMB 上下文。* @param preserveStructure 是否保持目录结构。* @param rootRemotePath   搜索的根远程路径,用于计算相对路径。* @param fileType   文件类型,指定只下载以该后缀的文件。* @throws IOException  如果发生 IO 错误。* @throws SmbException 如果发生 SMB 错误。*/private static void findAndDownloadHexFiles(SmbFile currentRemoteDir,Path localBasePath,CIFSContext context,boolean preserveStructure,String rootRemotePath,String fileType) throws IOException {log.info("进入递归方法开始查询!!!");// --- 确保目录 URL 以 '/' 结尾,这是 listFiles 的关键 ---String dirUrl = currentRemoteDir.getURL().toString();SmbFile dirToList = currentRemoteDir;if (!dirUrl.endsWith("/")) {dirToList = new SmbFile(dirUrl + "/", context);}// -------------------------------------------------------------SmbFile[] children;try {children = dirToList.listFiles(); // 列出子项System.out.println("列出目录内容: " + dirToList.getCanonicalPath());} catch (SmbException e) {System.err.println("❌ SmbException while listing children of: " + dirToList.getCanonicalPath() + " - " + e.getMessage());// 可以选择跳过此目录或抛出异常// 这里选择打印错误并跳过System.err.println(" -> 跳过此目录。");return;}if (children != null) {for (SmbFile child : children) {String childName = child.getName();// 过滤掉 . 和 ..if (".".equals(childName) || "..".equals(childName)) {continue;}if (child.isDirectory()) {// 递归进入子目录findAndDownloadHexFiles(child, localBasePath, context, preserveStructure, rootRemotePath,fileType);} else if (child.isFile() && childName.toLowerCase().endsWith(fileType)) {// 找到 .hex 文件,准备下载System.out.println("🔍 找到 .hex 文件: " + child.getCanonicalPath());// 计算本地文件路径Path localFilePath;if (preserveStructure) {// 计算相对于搜索根目录的路径String relativePath = child.getCanonicalPath().substring(rootRemotePath.length());// 清理路径分隔符 (确保使用本地分隔符)relativePath = relativePath.replace('/', File.separatorChar).replace('\\', File.separatorChar);localFilePath = localBasePath.resolve(relativePath);} else {// 直接放在基础目录下localFilePath = localBasePath.resolve(childName);}// 确保本地文件的父目录存在Path parentDir = localFilePath.getParent();if (parentDir != null) {Files.createDirectories(parentDir);}// 下载文件System.out.println("📥 下载到: " + localFilePath);try (InputStream in = child.getInputStream();OutputStream out = new BufferedOutputStream(new FileOutputStream(localFilePath.toFile()))) {byte[] buffer = new byte[8192];int bytesRead;while ((bytesRead = in.read(buffer)) != -1) {out.write(buffer, 0, bytesRead);}System.out.println("✅ 下载完成: " + childName);} catch (IOException e) {System.err.println("❌ 下载文件失败: " + child.getCanonicalPath() + " - " + e.getMessage());// 可以选择继续下载其他文件或抛出异常// 这里选择打印错误并继续System.err.println(" -> 继续下载其他文件。");}}}} else {System.out.println("⚠️  目录 " + dirToList.getCanonicalPath() + " 列表为空或无法访问。");}}
}

3、关键代码逻辑深度解析


3.1. 路径标准化处理(核心防错点)

remoteBasePath = remoteBasePath.replaceAll("^[\\\\/]+|[\\\\/]+$", "");
String targetSmbUrl = baseSmbUrl + (remoteBasePath.isEmpty() ? "" : remoteBasePath.replace("\\", "/") + "/");
  • 为什么必须:SMB 协议要求目录路径必须以 / 结尾,否则 listFiles() 会返回 SmbException: The system cannot find the file specified

  • 陷阱规避:处理了 Windows 路径分隔符(\)与 URL 标准分隔符(/)的混合问题


3.2. 递归遍历的防御性设计

if (!dirUrl.endsWith("/")) {dirToList = new SmbFile(dirUrl + "/", context);
}
  • 关键作用:确保每次遍历的目录 URL 都以 / 结尾,避免因路径格式错误导致的遍历中断

  • 错误案例:当远程路径为 smb://host/share/dir(缺少结尾/)时,listFiles() 会失败


3.3. 目录结构保持的精准实现

String relativePath = child.getCanonicalPath().substring(rootRemotePath.length());
relativePath = relativePath.replace('/', File.separatorChar);
localFilePath = localBasePath.resolve(relativePath);
  • 逻辑核心:通过 substring 精确截取相对路径(从根路径开始的后缀)

  • 平台适配replace('/', File.separatorChar) 确保在 Windows/Linux 系统都能正确生成本地路径


3.4. 文件过滤的精准匹配

childName.toLowerCase().endsWith(fileType)
  • 设计优势:大小写不敏感匹配(.HEX/.Hex/.hex 均被识别)

  • 安全边界:避免正则表达式导致的性能问题(endsWith 是 O(1) 操作)


3.5. 错误隔离机制(企业级健壮性)

try {// 下载文件
} catch (IOException e) {System.err.println("❌ 下载失败: " + child.getCanonicalPath() + " - " + e.getMessage());System.err.println(" -> 继续下载其他文件。");
}
  • 关键价值:单个文件下载失败(如文件被锁定)不会导致整个目录遍历中断

  • 对比:若未做此隔离,一个文件失败将导致整个任务失败


3.6. 资源安全释放

try (InputStream in = child.getInputStream();OutputStream out = new BufferedOutputStream(...)) {// 传输数据
}
  • Java 7 try-with-resources:确保 InputStreamOutputStream 在作用域结束时自动关闭
  • 避免泄漏:防止因未关闭流导致的文件句柄耗尽

3.7 代码设计决策总结

代码段 设计决策 为什么重要
`replaceAll("[1]+ [\/]+$", "")` 路径两端标准化
dirUrl.endsWith("/") 检查 目录 URL 标准化 确保 listFiles() 能正确识别目录
child.getCanonicalPath().substring() 精确计算相对路径 保持原始目录结构不丢失
toLowerCase().endsWith() 文件类型匹配 处理大小写不敏感的文件名
try-with-resources 流资源自动关闭 防止文件句柄泄漏
独立文件异常捕获 错误隔离 保证单个文件失败不影响整体任务

核心工程哲学:在 SMB 传输中,路径格式错误隔离是决定系统是否能稳定运行的两个关键因素。本实现通过精准处理路径和设计错误隔离机制,确保在工业环境中(如测试设备频繁生成文件)也能可靠运行。


4. 异常处理与最佳实践

4.1 常见异常

异常类型 可能原因 解决方案
jcifs.smb.SmbAuthExceptionunknown user name or bad password 用户名/密码错误,或无权限 检查账户权限,确认共享设置
jcifs.smb.SmbException: Access is denied 账户有登录权限但无文件访问权限 联系管理员授予“读取”或“完全控制”权限
SmbException: The system cannot find the file specified 1、目录 URL 未以 / 结尾;
2、这个目录在远程并不存在
确保 listFiles() 前 URL 以 / 结尾
UnknownHostException 主机名无法解析 检查 IP 或 DNS 配置
ConnectException 网络不通或防火墙阻断 确认 445/TCP 端口开放
The filename, directory name, or volume label syntax is incorrect 提供的文件或目录名称不符合语法要求(包含非法字符)。如 `< > : " ? * `)、URL 编码问题。

4.2 安全建议

  • 禁止硬编码密码:使用配置中心、环境变量或密钥管理服务;
  • 最小权限原则:SMB 账户仅授予必要读写权限;
  • 启用 SMB 签名(如需):在 jcifs-ng 中可通过 withProperties() 配置;
  • 避免使用 SMBv1jcifs-ng 默认禁用 SMBv1,符合安全规范。

4.3 性能优化

  • 使用缓冲流(BufferedInputStream)提升大文件传输效率;
  • 对大量小文件,考虑压缩后传输再解压;
  • 控制并发连接数,避免对 SMB 服务器造成压力。

  1. \/ ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/958814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Round 21 解题报告

T1 首先,我们看一下限制:“对于任意区间,B 的票数比 A 多不超过 \(k\) 张”。套路地,我们把 \(B\) 看作 \(1\),把 \(A\) 看作 \(-1\),限制转化为任意一个区间,区间和不超过 \(k\)。 那么我们试分析:在存在不满…

应对 “读放大” 问题的新方法 —— OceanBase 中的 Merge-On-Write 表

应对 “读放大” 问题的新方法 —— OceanBase 中的 Merge-On-Write 表为大家介绍一个 OceanBase 中的新特性 Merge-On-Write,用于大幅缓解 LSM Tree 存储架构下的读放大(buffer 表)问题~背景 OceanBase 从 4.3.0 版…

2025 年 11 月鞋样设计开发培训权威推荐榜:鞋样设计/3D开版/出格培训/打版教学机构实力解析与口碑之选

行业背景与发展趋势鞋样设计开发作为鞋业产业链的核心环节,近年来随着数字化技术的深度应用正经历着深刻变革。传统的手工打版模式正逐步向3D数字化设计开发转型,鞋样设计技术、电脑开版技术、出格技术等专业能力成为…

pg_auto_failover 环境变量导致的show命令错误

pg_auto_failover 环境变量导致的show命令错误今天遇到一个看似奇怪的问题,经过反复的重启,甚至重启操作系统,重装pg_auto_failover,都没有解决,冷静下来思考了一会,才发现是环境变量导致的,有必要记录一下 环境…

2025 年 11 月鞋业设计技术培训学校推荐排行榜,鞋业设计/技术培训,鞋业加盟公司推荐,专业教学与创业支持口碑之选

鞋业作为传统制造业的重要组成部分,近年来在技术升级与设计创新浪潮中迎来新的发展机遇。随着消费者对鞋类产品个性化、舒适度和时尚感要求的提升,鞋业设计技术培训与专业人才培养成为行业关注焦点。同时,鞋业加盟模…

2025 年 11 月阁楼货架,托盘式货架,横梁式货架厂家最新推荐,聚焦资质、案例、售后的五家机构深度解读!

引言 随着物流仓储行业对高效存储需求的不断升级,阁楼货架、托盘式货架、横梁式货架作为核心存储设备,其品质与服务成为企业关注焦点。为帮助企业精准筛选优质厂家,行业协会联合专业测评机构开展了 2025 年度专项测…

2025 年 11 月仓储货架,重型货架,货架托盘厂家最新推荐,聚焦资质、案例、售后的五家机构深度解读!

引言 随着物流仓储行业对高效存储需求的持续攀升,仓储货架、重型货架、货架托盘作为核心存储设备,其产品品质与厂家服务能力直接影响企业仓储效率。为帮助企业精准筛选优质厂家,本次测评联合行业权威协会,采用 “资…

2025 年 11 月优力胶厂家推荐排行榜,防静电优力胶,高硬度优力胶,专业定制与优质服务口碑之选

随着制造业向高端化、精密化方向发展,优力胶(聚氨酯弹性体)作为重要的工程材料,在工业领域的应用日益广泛。特别是在防静电和高硬度应用场景中,优力胶的性能直接关系到生产效率和产品质量。本文将从行业技术发展趋…

2025 年 11 月尼龙板厂家推荐排行榜,防静电尼龙板,透明尼龙板,白色尼龙板,耐磨尼龙板公司推荐

一、行业背景与发展趋势尼龙板作为工程塑料的重要分支,在工业制造领域扮演着关键角色。随着制造业的转型升级,防静电尼龙板在电子工业、精密仪器领域的应用日益广泛;透明尼龙板因其优异的透光性和机械性能,在医疗设…

微信公众号文章一篇最多放几个视频?

当我们在公众号文章后台上传视频时,有小伙伴就会问了:一篇公众号图文到底能上传几个视频?小编来告诉你: 一篇图文里面最多放10个视频。在后台上传视频时,我们可以看到上限为10篇。一篇图文最多5万字,如果是单独发…

2025年11月除锈剂厂家推荐排行榜:专业解析钢铁、金属、不锈钢等材料除锈解决方案

在工业生产与设备维护领域,除锈剂作为金属表面处理的关键化学品,其性能优劣直接影响设备使用寿命与生产效率。随着制造业技术升级,市场对除锈剂的专业化、精细化需求日益提升,特别是在钢铁、金属、钢材、不锈钢、铆…

2025 年 11 月研磨膏厂家推荐排行榜,金刚石研磨膏,油性金刚石研磨膏,水性金刚石研磨膏公司推荐

行业背景分析研磨材料行业作为精密制造领域的关键支撑产业,近年来随着高端装备制造、半导体、光学器件等行业的快速发展,迎来了新一轮技术革新与市场需求增长。金刚石研磨膏作为精密研磨抛光的关键材料,其性能直接影…

2025 年 11 月研磨液厂家推荐排行榜,金刚石研磨液,水性金刚石,油性金刚石,氧化铝,二氧化硅,钢铁研磨液,无芯研磨液,振动研磨液,五金工具研磨液公司推荐

在精密制造与表面处理行业,研磨液作为关键工艺辅料,其性能直接影响工件加工质量与生产效率。随着新材料与新工艺的不断涌现,研磨液市场呈现出多元化与专业化并进的发展态势。本文基于行业技术标准与市场反馈,对金刚…

555定时器-1 555定时器简介

555定时器-1 555定时器简介参考:https://www.circuitbread.com/tutorials/555-timer-1-introduction-to-555-timers 555定时器-1 555定时器简介555定时器是一种非常流行的做时序相关应用的IC。它们坚固耐用,可用于任…

2025年北京唯宝智能马桶公司权威推荐榜单:annwa智能马桶/科勒卫浴/vivi马桶源头公司精选

在消费升级与健康家居需求持续增长的背景下,高端智能马桶作为卫浴空间的核心产品,其技术性能与服务体系已成为消费者关注的重点。行业数据显示,2024-2029年中国智能马桶市场年均复合增长率预计将保持在25%以上,家庭…

Looper、MessageQueue、Message及Handler的关系是什么?如何保证MessageQueue的并发访问安全? - 教程

Looper、MessageQueue、Message及Handler的关系是什么?如何保证MessageQueue的并发访问安全? - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: blo…

基于MATLAB生成雷达脉冲信号

一、基础脉冲信号生成 1. 矩形脉冲生成 % 参数设置 fs = 1e9; % 采样频率 (Hz) Tp = 1e-6; % 脉冲宽度 (s) fc = 10e9; % 载波频率 (Hz) t = 0:1/fs:2*Tp; % 时间向量 (包含脉冲前后各…

2025年食品级消毒酒精权威推荐榜单:免洗速干手消毒液/卫生手消毒液/国产消毒液源头厂家精选

在食品加工与餐饮行业,一瓶安全高效的食品级消毒酒精,是保障卫生安全的隐形守护者。 食品级消毒酒精作为食品安全生产的关键防护产品,其纯度标准与生产工艺直接关系到食品安全和消费者健康。随着全球对食品安全要求…

「机器学习笔记7」决策树学习:从理论到实践的全面解析(上) - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

微算法科技(NASDAQ MLGO)基于PoS的跨链桥接协议(PoS-BCP):重塑区块链互操作生态

区块链技术的快速发展催生了多元的链上生态,从公链到联盟链,从通用型区块链到垂直领域专用链,不同网络在架构设计、共识机制与功能定位上呈现显著差异。这种多样性虽推动了行业创新,却也导致“数据孤岛”现象——资…