近期在做公司项目时,有个工作站文件、数据采集的功能需求,文件会被存储在局域网内某台电脑的data/{yyyyMMdd}/...文件夹下,yyyyMMdd代表当天的日期,百度查了下,通常有两种方式可以实现:
(1)FTP的方式,就是把每台设备都安装一下FTP服务搞成一个FTP文件服务器,这种方式操作起来会比较麻烦,而且用户自己不太会操作
(2)采用共享文件夹的方式,这种方式更简单一些
于是决定采用第二种方式,使用的依赖是SMBJ,dependency是
<!--支持文件共享SMB1协议-->
<!--<dependency>
<groupId>org.samba.jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.17</version>
</dependency>-->
<!--支持文件共享SMB2/3协议-->
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>smbj</artifactId>
<version>0.10.0</version>
</dependency>
本来考虑使用jcifs,但其支持的是SMB1协议,查阅了其官网发现win10系统已抛弃了SMB1协议而采用SMB2/3协议,并且在该网站推荐使用smbj,因此决定使用smbj来实现访问共文件夹。GitHub地址可参考:https://github.com/hierynomus/smbj
If you're looking for the latest and greatest open source Java SMB library,
this is not it. JCIFS has been in maintenance-mode-only for several years and although what it does support works fine (SMB1, NTLMv2, midlc, MSRPC and various utility classes),
jCIFS does not support the newer SMB2/3 variants of the SMB protocol which is slowly becoming required (Windows 10 requires SMB2/3).
JCIFS only supports SMB1 but Microsoft has deprecated SMB1 in their products.
So if SMB1 is disabled on your network, JCIFS' file related operations will NOT work.
Fortunately there are multiple new open source SMB Java projects to try including the following:
比较之后决定使用smbj来实现,在此之前需要做一些准备工作。
(1)把文件夹设置为共享文件夹,并且最好设置为密码访问的方式,否则局域网内所有电脑都能访问该文件夹。
指定为共享文件夹,选择共享给哪个用户,默认是需要账号密码的,如果你的电脑用户都是无密码的,那么你可以选择让该文件夹设置为不共享,如果你希望是需要密码的那么你最好自己新建个用户。
(2) 如果选择有密码的共享,本地用户本来就有密码那在代码里使用该用户密码访问就可以,如果没有的话可以新建一个带密码的用户。
(2)
如下是编写的访问共享文件的代码部分。
String equiptIp = "10.x.x.x";
String authUserName = "xxx";
String authPwd = "xxx";
String shareDir = "share";
Connection connection = null;
Session session = null;
DiskShare dirShare = null;
try {
connection = client.connect(equiptIp);
} catch (IOException e1) {
LOGGER.info(equiptIp + "连接失败!");
e1.printStackTrace();
return ;
}
LOGGER.info(equiptIp + "连接成功:" + connection.isConnected());
AuthenticationContext ac = new AuthenticationContext(authUserName, authPwd.toCharArray(), null); //null位置是输入域名的,没有可为空
try {
session = connection.authenticate(ac);
} catch (Exception e) {
LOGGER.info(equiptIp + "认证失败失败!");
e.printStackTrace();
continue;
}
LOGGER.info(equiptIp + "认证成功!");
try {
dirShare = (DiskShare) session.connectShare(shareDir);
} catch (Exception e) {
LOGGER.info(equiptIp + "读取共享文件夹" + shareDir +"失败!");
e.printStackTrace();
continue;
}
String fileDir = "20210906"+ "/dir1";
if (dirShare.folderExists(fileDir)) {
Directory directory = dirShare.openDirectory(fileDir, EnumSet.of(AccessMask.FILE_LIST_DIRECTORY), null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
List<FileIdBothDirectoryInformation> list = directory.list();
for (FileIdBothDirectoryInformation f : list) {
String fileName = f.getFileName(); // 文件名字
String fileUrl = fileDir + "/" + fileName;
if (!".".equals(fileName) && !"..".equals(fileName) && dirShare.fileExists(fileUrl)) {
// 这里就可以拿到文件流了,可以读取
File smbFileRead = dirShare.openFile(fileUrl, EnumSet.of(AccessMask.GENERIC_READ), null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
InputStream in = smbFileRead.getInputStream();
// ......
}
}
}
try {
if (dirShare != null) {
dirShare.close();
}
if (session != null) {
session.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
在拿到inputStream后就可以读取文件到应用服务器了,之后的操作可以按照我们的业务来编写代码。
由于采用的定时任务,为防止重复读取文件,我的业务代码里使用(fileName + 系统业务的一个相关ID + 当天日期yyyyMMdd)做MD5加密得到一个字符串作为这个文件存储数据表的ID,每次访问共享文件的时候会作比较。是否已读取过。
需求中还包括CSV数据采集和Access数据采集,后续更新。