问题
需要访问服务器的nas盘,是smb协议的,用的jsifs包,上网找了方法使用后报错jcifs.smb.SmbException: Failed to connect: 0.0.0.0<00>/IP
原因
是jsifs仅支持SMB1,不支持SMB2/SMB3,没办法改服务器协议,所以选择使用smbj包。
依赖如下
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>smbj</artifactId>
<version>0.13.0</version>
</dependency>
github仓库:https://github.com/hierynomus/smbj
代码如下
下面是根据仓库的demo和网上找的内容自己写了一个小的工具类,不过后面又不用这种方式访问了,所以没有得到充分的测试。
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@Configuration
public class NasConfig {
/**
* IP地址
*/
@Value("${nas.hostName}")
private final static String hostName = "xxx";
/**
* 目标用户名
**/
@Value("${nas.username}")
private final static String username = "xxx";
/**
* 密码
**/
@Value("${nas.password}")
private final static String password = "xxx";
/**
* 超时设置读,写和Transact超时(默认为60秒)
**/
@Value("${nas.timeout}")
private final static int timeout = 120;
/**
* Socket超时(默认为0秒)
**/
@Value("${nas.soTimeout}")
private final static int soTimeout = 180;
@Bean(name = "SmbSession")
private static Session getSession() {
SmbConfig config = SmbConfig.builder()
.withTimeout(timeout, TimeUnit.SECONDS) // 超时设置读,写和Transact超时(默认为60秒)
.withSoTimeout(soTimeout, TimeUnit.SECONDS) // Socket超时(默认为0秒)
.build();
SMBClient client = new SMBClient(config);
Connection connection = null;
try {
connection = client.connect(hostName);
} catch (IOException e) {
throw new RuntimeException(e);
}
AuthenticationContext ac = new AuthenticationContext(username, password.toCharArray(), "");
return connection.authenticate(ac);
}
}
import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.mssmb2.SMBApiException;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.SmbConfig;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.share.File;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* SMB2CreateDisposition 可选参数及解释
* 如果该文件已经存在,它取代。 否则,创建该文件。 此值不应该被用于打印机对象
* FILE_SUPERSEDE(0x00000000L),
* 如果该文件已经存在,返回成功; 否则,操作失败。 绝不能用于打印机对象
* FILE_OPEN(0x00000001L),
*如果该文件已经存在,操作失败; 否则,创建该文件。
* FILE_CREATE(0x00000002L),
* 打开该文件,如果它已经存在; 否则,创建该文件。 此值不应该被用于打印机对象
* FILE_OPEN_IF(0x00000003L),
* 覆盖该文件,如果它已经存在; 否则,操作失败。 绝不能用于打印机对象。
* FILE_OVERWRITE(0x00000004L),
* 覆盖该文件,如果它已经存在; 否则,创建该文件。 此值不应该被用于打印机对象。
*/
@Component
@Slf4j
public class NasUtils {
@Resource(name = "SmbSession")
private Session session;
/**
* 列出指定SMB共享中路径下的文件,根据搜索模式进行过滤,并返回文件列表
*
* @param shareName SMB共享的名称,例如 "\\\\server\\share"
* @param path 在共享内的路径,例如 "documents"
* @param searchPattern 文件名匹配模式,如 "*.txt" 用于查找所有文本文件
* @return 文件信息列表,包含匹配搜索模式的所有文件
*/
public List<FileIdBothDirectoryInformation> listFiles(String shareName, String path, String searchPattern) {
try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
return share.list(path, searchPattern);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 从NAS读取指定文件的内容并转为String
*
* @param shareName 网络共享文件夹的名称
* @param path 文件在共享文件夹中的路径
* @param fileName 要读取的文件名
* @return 文件内容的字符串表示
*/
public String readFileFromNas(String shareName, String path, String fileName) {
try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
File file = share.openFile(shareName + java.io.File.separator + path + java.io.File.separator + fileName,
new HashSet<>(List.of(AccessMask.GENERIC_ALL)),
null,
SMB2ShareAccess.ALL,
SMB2CreateDisposition.FILE_OPEN,
null);
StringBuilder content = new StringBuilder();
try (InputStream inputStream = file.getInputStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
InputStreamReader inputStreamReader = new InputStreamReader(bufferedInputStream, StandardCharsets.UTF_8); // 使用合适的字符编码
BufferedReader reader = new BufferedReader(inputStreamReader)) {
String currentLine;
while ((currentLine = reader.readLine()) != null) {
content.append(currentLine);
}
return content.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 上传文件到NAS
*
* @param shareName NAS共享文件夹名
* @param path 目标路径
* @param fileName 文件名
* @param localPath 本地文件路径
* @return 如果上传成功返回true,否则返回false
*/
public boolean uploadFileToNas(String shareName, String path, String fileName, String localPath) {
try (DiskShare share = (DiskShare) session.connectShare(shareName);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(localPath + fileName))) {
File f = null;
// 文件不存在则创建,存在则打开。
try {
System.out.println(path + java.io.File.separator + fileName);
f = share.openFile(shareName + java.io.File.separator + path + java.io.File.separator + fileName, new HashSet<>(
List.of(AccessMask.GENERIC_ALL)),
null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_CREATE,
null);
} catch (SMBApiException e) {
// 检查异常是否表示文件已存在
if (e.getMessage().contains("STATUS_OBJECT_NAME_COLLISION")) {
System.out.println("文件已经存在执行打开操作");
System.out.println(path + java.io.File.separator + fileName);
f = share.openFile(shareName + java.io.File.separator + path + java.io.File.separator + fileName, new HashSet<>(
List.of(AccessMask.GENERIC_ALL)),
null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OVERWRITE,
null);
} else {
log.error(e.getMessage());
return false;
}
}
if (f != null) {
OutputStream outputStream = f.getOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = bis.read(buffer, 0, buffer.length)) != -1) {
outputStream.write(buffer, 0, len);
}
outputStream.flush();
outputStream.close();
System.out.println("上传了文件");
return true;
} else {
return false;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 从NAS下载单个文件
*
* @param shareName 共享名
* @param path NAS上的文件路径
* @param fileName 文件名
* @param localPath 本地保存路径
*/
public void downloadFileFromNas(String shareName, String path, String fileName, String localPath) {
// 构建NAS上的文件完整路径
String filePath = shareName + java.io.File.separator + path + java.io.File.separator + fileName;
// 构建本地保存的文件路径
String dstPath = localPath + java.io.File.separator + fileName;
try (DiskShare share = (DiskShare) session.connectShare(shareName);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dstPath))) {
// 检查文件是否存在
if (share.fileExists(filePath)) {
System.out.println("正在下载文件:" + filePath);
// 打开NAS上的文件并获取输入流
File smbFileRead = share.openFile(filePath, EnumSet.of(AccessMask.GENERIC_READ), null,
SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
InputStream in = smbFileRead.getInputStream();
// 读取并写入文件内容
byte[] buffer = new byte[4096];
int len = 0;
while ((len = in.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("文件下载成功");
System.out.println("==========================");
} else {
System.out.println("文件不存在");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 从NAS的指定路径下载匹配指定模式的文件
*
* @param shareName 共享文件夹名
* @param path NAS上的目录路径
* @param localPath 本地保存路径
* @param searchPattern 文件搜索模式
*/
public void downloadAllFilesFromNas(String shareName, String path, String localPath, String searchPattern) {
try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
// 遍历NAS上匹配的文件
for (FileIdBothDirectoryInformation f : share.list(path, searchPattern)) {
// 构建NAS上的完整文件路径
String filePath = shareName + path + f.getFileName();
// 构建本地保存的文件路径
String dstPath = localPath + f.getFileName();
// 创建本地文件输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dstPath));
// 检查文件是否存在
if (share.fileExists(filePath)) {
System.out.println("正在下载文件:" + f.getFileName());
// 打开NAS上的文件并获取输入流
File smbFileRead = share.openFile(filePath, EnumSet.of(AccessMask.GENERIC_READ), null,
SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null);
InputStream in = smbFileRead.getInputStream();
// 读取并写入文件内容
byte[] buffer = new byte[4096];
int len = 0;
while ((len = in.read(buffer, 0, buffer.length)) != -1) {
bos.write(buffer, 0, len);
}
bos.flush();
bos.close();
System.out.println("文件下载成功");
System.out.println("==========================");
} else {
System.out.println("文件不存在");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除指定共享目录下的文件
*
* @param shareName 共享目录名称
* @param path 文件路径
*/
public void deleteFile(String shareName, String path, String fileName) {
try (DiskShare share = (DiskShare) session.connectShare(shareName)) {
share.rm(path + fileName);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
除了GitHub,还可以参考的文章: