· SMB 协议
SMB(Server Message Block)协议,主要用途是文件共享,但除了文件共享还包括:
· 数据包/版本协商(用于确定服务器和客户端都支持的具有最高功能级别的数据包版本)
· 确定网络上的其他Microsoft SMB协议服务器或网络浏览
· 网络打印
· 文件,目录和共享访问身份验证
· 文件和记录锁定
· 文件和目录更改通知
· 扩展文件属性处理
· Unicode支持
· 机会锁(协调客户端和服务器的数据一致性,使服务器和客户端数据同步)
SMB 是一种请求-响应(客户端-服务端)协议。通过 SMB 协议,客户端应用程序可以在各种网络环境下读、写服务器上的文件,以及对服务器程序提出服务请求。此外通过 SMB 协议,应用程序可以访问远程服务器端的文件、以及打印机等资源。
SMB默认使用端口TCP/139和TCP/445端口,SMB协议目前共三个版本,本文对SMB1.0版本协议进行应用实现。
SMB协议简介在网上有很多介绍,各位可以自行进行百度查看。
· 创建共享文件
共享文件创建在Win10系统中,Win10中创建共享文件相对比较简单,选中需要共享的文件夹右键选择‘属性’:
首选点击’共享(S)…‘按钮出现右侧界面,在右侧界面,选择对应的用户并设置用户权限,最后点击’共享(H),一直‘下一步’就完成了共享文件的设置。
需要注意的是由于SMB1.0版本协议已处于落伍边缘,Win10默认是不开启SMB1.0的,需要手动开启,在控制面板‘程序与功能’中选择开启即可:
· Maven 依赖
<dependency>
<groupId>jcifsgroupId>
<artifactId>jcifsartifactId>
<version>1.3.17version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.4version>
dependency>
· Java SMB 工具类
package com.arhorchin.securitit.protocol.smb;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.apache.log4j.Logger;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import jcifs.smb.SmbFileOutputStream;
public class SmbUtil {
/**
* logger.
*/
private static Logger logger = Logger.getLogger(SmbUtil.class);
/**
* SMB下载文件.
* @param remoteIp 远程IP.
* @param remoteUrl 远程地址.
* @param username 远程用户.
* @param password 远程密码.
* @param filename 远程文件.
* @return 共享文件.
*/
public static byte[] smbDownload(String remoteIp, String remoteUrl, String username, String password,
String filename) {
SmbFile remoteFile = null;
InputStream remoteFileIs = null;
ByteArrayOutputStream remoteFileBaos = null;
NtlmPasswordAuthentication remoteAuth = null;
try {
remoteAuth = new NtlmPasswordAuthentication(remoteIp, username, password);
remoteFile = new SmbFile(remoteUrl, remoteAuth);
if (!remoteFile.exists()) {
remoteFile.mkdirs();
}
remoteFile = new SmbFile(remoteUrl + "//" + filename, remoteAuth);
remoteFileBaos = new ByteArrayOutputStream();
remoteFileIs = new BufferedInputStream(new SmbFileInputStream(remoteFile));
byte[] buffer = new byte[1024];
while (remoteFileIs.read(buffer) != -1) {
remoteFileBaos.write(buffer);
buffer = new byte[1024];
}
return remoteFileBaos.toByteArray();
} catch (Exception ex) {
logger.error(ex);
return null;
} finally {
try {
remoteFileIs.close();
remoteFileBaos.close();
} catch (Exception ex) {
logger.error(ex);
}
}
}
/**
* SMB上传文件.
* @param remoteIp 远程IP.
* @param remoteUrl 远程地址.
* @param username 远程用户.
* @param password 远程密码.
* @param filename 远程文件.
* @param fileBytes 待上传文件.
* @return 上传结果.
*/
public static boolean smbUpload(String remoteIp, String remoteUrl, String username, String password,
String filename, byte[] fileBytes) {
SmbFile remoteFile = null;
BufferedOutputStream remoteFileBos = null;
NtlmPasswordAuthentication remoteAuth = null;
try {
remoteAuth = new NtlmPasswordAuthentication(remoteIp, username, password);
remoteFile = new SmbFile(remoteUrl, remoteAuth);
if (!remoteFile.exists()) {
remoteFile.mkdirs();
}
remoteFile = new SmbFile(remoteUrl + "//" + filename, remoteAuth);
remoteFileBos = new BufferedOutputStream(new SmbFileOutputStream(remoteFile));
remoteFileBos.write(fileBytes);
return true;
} catch (Exception ex) {
logger.error(ex);
ex.printStackTrace();
return false;
} finally {
try {
remoteFileBos.close();
} catch (Exception ex) {
logger.error(ex);
}
}
}
/**
* SMB删除文件.
* @param remoteIp 远程IP.
* @param remoteUrl 远程地址.
* @param username 远程用户.
* @param password 远程密码.
* @param filename 远程文件.
* @return 删除结果.
*/
public static boolean smbDelete(String remoteIp, String remoteUrl, String username, String password,
String filename) {
SmbFile remoteFile = null;
NtlmPasswordAuthentication remoteAuth = null;
try {
remoteAuth = new NtlmPasswordAuthentication(remoteIp, username, password);
remoteFile = new SmbFile(remoteUrl, remoteAuth);
if (!remoteFile.exists()) {
remoteFile.mkdirs();
}
remoteFile = new SmbFile(remoteUrl + "//" + filename, remoteAuth);
remoteFile.delete();
return true;
} catch (Exception ex) {
logger.error(ex);
return false;
}
}
}
测试类如下:
package com.arhorchin.securitit.protocol.smb;
import java.io.File;
import org.apache.commons.io.FileUtils;
public class SmbUtilTester {
public static void main(String[] args) throws Exception {
String remoteIp = "127.0.0.1";
String remoteUrl = "smb://127.0.0.1/Storage/";
String username = "xxxxxx"; // 登录用户名.
String password = "xxxxxx"; // 登录密码.
String filename = "demo.pdf";
byte[] fileBytes = FileUtils.readFileToByteArray(new File("C:/Users/Administrator/Downloads/个人文件/demo.pdf"));
// 上传文件.
SmbUtil.smbUpload(remoteIp, remoteUrl, username, password, filename, fileBytes);
// 下载文件.
byte[] downBytes = SmbUtil.smbDownload(remoteIp, remoteUrl, username, password, filename);
System.out.println(downBytes.length);
// 删除文件.
SmbUtil.smbDelete(remoteIp, remoteUrl, username, password, filename);
}
}
上面测试类执行的同时,观察共享文件夹,可以看到:
1) 共享文件夹首先上传了一个文件demo.pdf。
2) 测试类从共享文件夹下载了刚才上传的文件,并输出了文件长度。
3) 共享文件夹内的文件被删除掉了。
· 总结
· jcifs:jcifs依赖只能支持SMB1.0版本协议的实现,在实际应用时需要注意所用协议版本。
· SmbUtil工具类可以直接复制使用,已经经过简单测试,只需按照文中要求导入Maven依赖即可。
· SmbUtil中写法已经避免了用户名和密码中包含特殊字符的问题,如果使用完整地址方式访问,无法有效处理用户名和密码中的特殊字符。