文章目录
tips: 集成springboot与不集成springboot区别不大,springboot中无非是引入一个maven依赖 加一个@Component注解 , 默认是单例;
复制代码前 请先认真看注意事项
java的sftp传输
依赖:
<!-- sftp -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.49</version>
</dependency>
核心代码:
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Properties;
import java.util.Vector;
@Slf4j
@Component
public class SftpUtils {
private Session sshSession;
private ChannelSftp sftp;
/**
* 连接sftp服务器
*
* @param host
* @param port
* @param username
* @param password
* @return
* @throws Exception
*/
public ChannelSftp connect(String host, int port, String username, String password) throws Exception {
JSch jsch = new JSch();
sshSession = jsch.getSession(username, host, port);
log.debug("Session created.");
sshSession.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
sshSession.connect();
log.debug("Session connected.");
log.debug("Opening Channel.");
Channel channel = sshSession.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
log.debug("Connected to " + host + ".");
return sftp;
}
/**
* 连接sftp服务器
*
* @param host
* @param port
* @param username
* @param privateKey
* @param passphrase
* @return
* @throws Exception
*/
public ChannelSftp connect(String host, int port, String username, String privateKey, String passphrase) throws Exception {
JSch jsch = new JSch();
//设置密钥和密码
if (!StringUtils.isEmpty(privateKey)) {
if (!StringUtils.isEmpty(passphrase)) {
//设置带口令的密钥
jsch.addIdentity(privateKey, passphrase);
} else {
//设置不带口令的密钥
jsch.addIdentity(privateKey);
}
}
sshSession = jsch.getSession(username, host, port);
log.debug("Session created.");
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
sshSession.setConfig(sshConfig);
sshSession.connect();
log.debug("Session connected.");
log.debug("Opening Channel.");
Channel channel = sshSession.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
log.debug("Connected to " + host + ".");
return sftp;
}
public void portForwardingL(int lport, String rhost, int rport) throws Exception {
int assinged_port = sshSession.setPortForwardingL(lport, rhost, rport);
System.out.println("localhost:" + assinged_port + " -> " + rhost + ":" + rport);
}
/**
* 断开连接
*/
public void disconnect() {
if (sftp != null) sftp.disconnect();
if (sshSession != null) sshSession.disconnect();
}
/**
* 上传文件
*
* @param directory 服务器的目录路径
* @param uploadFile 要上传的文件路径
* <p>
* File path = new File(ResourceUtils.getURL("classpath:").getPath());
* path.getAbsolutePath() : 相对路径 即class目录下
*/
public void upload(String directory, String uploadFile) throws Exception {
sftp.cd(directory);
File file = new File(uploadFile);
sftp.put(new FileInputStream(file), file.getName());
}
/**
* 上传文件
*
* @param directory 服务器的目录路径
* @param file 要上传的文件 例如 new File("Dockerfile") 是springboot项目根目录下的Dockerfile文件
* @throws Exception
*/
public void upload(String directory, File file) throws Exception {
sftp.cd(directory);
sftp.put(new FileInputStream(file), file.getName());
log.info("upload file " + file.getAbsolutePath() + " to host " + sshSession.getHost());
}
//利用流上传文件 fileName
public void uploadFileInputStream(MultipartFile file, String directory, String fileName) throws Exception {
sftp.cd(directory);
sftp.put(file.getInputStream(), fileName);
}
public void uploadDir(File src, String dst) throws Exception {
if (!exist(dst)) {
sftp.mkdir(dst);
}
if (src.isFile()) {
upload(dst, src);
} else {
for (File file : src.listFiles()) {
if (file.isDirectory()) {
uploadDir(file, dst + "/" + file.getName());
}
upload(dst, file);
}
}
}
/**
* 目录是否查找
*
* @param path
* @return
* @throws SftpException
*/
public boolean exist(String path) throws SftpException {
String pwd = sftp.pwd();
try {
sftp.cd(path);
} catch (SftpException e) {
if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
return false;
} else {
throw e;
}
} finally {
sftp.cd(pwd);
}
return true;
}
/**
* 下载文件
*
* @param directory 服务器目录
* @param downloadFile 要下载的文件名
* @param saveFile 输出文件名
* @throws Exception
*/
public void download(String directory, String downloadFile, String saveFile) throws Exception {
try {
sftp.cd(directory);
File file = new File(saveFile);
sftp.get(downloadFile, new FileOutputStream(file));
} catch (SftpException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
disconnect();
}
}
/**
* 下载文件
*
* @param directory
* @param downloadFile
* @param saveFile
* @throws Exception
*/
public void download(String directory, String downloadFile, File saveFile) throws Exception {
try {
sftp.cd(directory);
sftp.get(downloadFile, new FileOutputStream(saveFile));
System.out.println("download file " + directory + "/" + downloadFile + " from host " + sshSession.getHost());
} catch (SftpException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
disconnect();
}
}
/**
* 下载文件
*
* @param src
* @param dst
* @throws Exception
*/
@SuppressWarnings("unchecked")
public void downloadDir(String src, File dst) throws Exception {
try {
sftp.cd(src);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dst.mkdirs();
Vector<ChannelSftp.LsEntry> files = sftp.ls(src);
for (ChannelSftp.LsEntry lsEntry : files) {
if (lsEntry.getFilename().equals(".") || lsEntry.getFilename().equals("..")) {
continue;
}
if (lsEntry.getLongname().startsWith("d")) {
downloadDir(src + "/" + lsEntry.getFilename(), new File(dst, lsEntry.getFilename()));
} else {
download(src, lsEntry.getFilename(), new File(dst, lsEntry.getFilename()));
}
}
}
/**
* 删除文件
*
* @param directory
* @param deleteFile
* @throws SftpException
*/
public void delete(String directory, String deleteFile) throws SftpException {
sftp.cd(directory);
sftp.rm(deleteFile);
}
/**
* 列出目录下的文件
*
* @param directory
* @return
* @throws SftpException
*/
public Vector listFiles(String directory) throws SftpException {
return sftp.ls(directory);
}
public Session getSshSession() {
return sshSession;
}
public ChannelSftp getSftp() {
return sftp;
}
}
这个使用比较简单 也没有什么歧义 使用示例就不再写了
sftp按时间过滤文件列表
该方法是受smb1.0中过滤方式的启发 并研究Sftp工具包api 编写的
我们可以看到 ChannelSftp中的ls(path)方法,默认是把所有的entry加入到Vector中,并调用了 ls(path,selector) 方法
所以我们可以看看entry里面到底有什么:
filename:文件名 longname:长文件名
再看看attrs里面是什么:
熟悉linux的同学 能很清楚知道mtime是什么 当然即使不熟悉 也能猜出是modifyTime修改时间的缩写, 既然提供了这些参数 那么我们可以稍加判断, 把符合条件的entry存入Vector即可
代码有点不符合我们的习惯,sftp(ls,selector)方法是没有返回值的,是在外部定义的vector中接收值,且mtime单位是秒 并不是毫秒 这一细节一度让博主怀疑人生。
代码如下:
(需要将代码复制到SftpUtils类中)
/**
* 列出(文件的最新修改时间)时间点之后的文件
*
* @param directory
* @return
* @throws SftpException
*/
public Vector listFilesLaterThenTime(String directory, long time) throws SftpException {
Vector vector = new Vector();
sftp.ls(directory, new ChannelSftp.LsEntrySelector() {
public int select(ChannelSftp.LsEntry entry) {
// 很坑的点 atime和mtime的单位是秒 并非我们常用的毫秒
// 从 getMtimeString()方法中的 mtime * 1000L 可以看出
long mTime = entry.getAttrs().getMTime() * 1000L;
if (mTime < time) {
vector.addElement(entry);
}
return 0;
}
});
return vector;
}
sftp注意事项
注: ftp和sftp是有区别的 ,linux中 ftp默认21端口 sftp默认是22端口,sftp传输加密 安全 效率较低,ftp传输不安全 效率较高, 本文讨论的是sftp 两种协议jar包引用不同。
java smb1.0传输
依赖:
<!-- samba 共享文件夹 -->
<dependency>
<groupId>jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.17</version>
</dependency>
简单封装了一个model类
model类代码:
/**
* smb传输 model类
* SMB地址写法:smb://用户名:密码@远程ip/地址/(或有无文件名)(注:如只获取文件夹后缀一定有“/”)
* <p>
*/
@Data
@EqualsAndHashCode
public class SMBConfig implements Serializable {
/**
* 服务器ip
*/
private String smbIp;
/**
* 服务器账号
*/
private String smbUser;
/**
* 服务器密码
*/
private String smbPwd;
/**
* 文件路径 在共享文件夹中的路径 (不带计算机名称)
*/
private String smbPath;
/**
* 文件名
*/
private String smbFile;
/**
* 输入流
*/
private InputStream is;
/**
* 输出流
*/
private OutputStream os;
public SMBConfig() {
}
}
核心代码:
// import SMBConfig类路径;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.*;
@Component
@Slf4j
public class SMBUtils {
/**
* 往samba上传文件
*
* @param url 服务器IP地址
* @param userName 用户登录名
* @param password 用户登录密码
* @param storePath 服务器文件存储路径
* @param fileName 服务器文件存储名称
* @param is 文件输入流
* @return true:上传成功
* <p>
* false:上传失败
*/
public static boolean storeFile(String url, String userName, String password, String storePath, String fileName, InputStream is) {
BufferedInputStream bf = null;
OutputStream os = null;
boolean result = false;
try {
UniAddress ua = UniAddress.getByName(url);
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(url, userName, password);
SmbSession.logon(ua, auth);
SmbFile sf = new SmbFile("smb://" + url + storePath + "/" + fileName, auth);
sf.connect();
SmbFile sffolder = new SmbFile("smb://" + url + storePath, auth);
if (!sffolder.exists())
sffolder.mkdirs();
bf = new BufferedInputStream(is);
os = sf.getOutputStream();
byte[] bt = new byte[1024];
int n = bf.read(bt);
while (n != -1) {
os.write(bt, 0, n);
os.flush();
n = bf.read(bt);
}
result = true;
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
try {
if (bf != null)
bf.close();
if (os != null)
os.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return result;
}
/**
* 从samba下载文件
*
* @param url 服务器IP地址
* @param userName 用户登录名
* @param password 用户登录密码
* @param remotePath 服务器文件存储路径
* @param fileName 服务器文件存储名称
* @param os 文件输出流
* @return true:下载成功
* <p>
* false:下载失败
*/
public static boolean retrieveFile(String url, String userName, String password, String remotePath, String fileName, OutputStream os) {
boolean result = false;
BufferedInputStream bf = null;
try {
UniAddress ua = UniAddress.getByName(url);
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(url, userName, password);
SmbSession.logon(ua, auth);
SmbFile sf = new SmbFile("smb://" + url + remotePath + "/" + fileName, auth);
if (sf.exists()) {
bf = new BufferedInputStream(sf.getInputStream());
byte[] bt = new byte[1024];
int n = bf.read(bt);
while (n != -1) {
os.write(bt, 0, n);
os.flush();
n = bf.read(bt);
}
result = true;
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
try {
if (bf != null)
bf.close();
if (os != null)
os.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return result;
}
/**
* 从samba删除文件
*
* @param url 服务器IP地址
* @param userName 用户登录名
* @param password 用户登录密码
* @param remotePath 服务器文件存储路径
* @param fileName 服务器文件存储名称
* @return true:删除成功
* <p>
* false:删除失败
*/
public static boolean deleteFile(String url, String userName, String password, String remotePath, String fileName) {
boolean result = false;
try {
UniAddress ua = UniAddress.getByName(url);
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(url, userName, password);
SmbSession.logon(ua, auth);
SmbFile sf = new SmbFile("smb://" + url + remotePath + "/" + fileName, auth);
sf.delete();
result = true;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
}
/**
* 往samba上传文件
*
* @param smbConfig
* @return
*/
public static boolean storeFile(SMBConfig smbConfig) {
return storeFile(smbConfig.getSmbIp(), smbConfig.getSmbUser(), smbConfig.getSmbPwd(), smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getIs());
}
/**
* 从samba下载文件
*
* @param smbConfig
* @return
*/
public static boolean retrieveFile(SMBConfig smbConfig) {
return retrieveFile(smbConfig.getSmbIp(), smbConfig.getSmbUser(), smbConfig.getSmbPwd(), smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getOs());
}
}
使用示例:
SMBConfig smbConfig = new SMBConfig();
smbConfig.setSmbIp("192.168.1.1");
smbConfig.setSmbPwd("123456");
smbConfig.setSmbUser("admin");
// 路径
smbConfig.setSmbPath("/test");
// 上传后的文件名叫这个
smbConfig.setSmbFile("pom");
// 将springboot项目中根目录的pom.xml文件上传
smbConfig.setIs(new FileInputStream("pom.xml"));
// 上传
storeFile(smbConfig);
注: smbPath 例如在共享文件夹中 路径为test 就写/test ,不用加上下图马赛克位置的计算机名
smb1.0按时间过滤文件列表
举个场景示例: 服务器上面有成千上万 甚至更多的文件,我们每过一段时间客户端去请求处理一次,那么下次请求 客户端就无需去处理之前已经处理过的文件了。
所以每次请求 需要记录这批文件中 最新的那个修改时间time, 下次去处理修改时间大于time的即可。
观察SmbFile类中的api,发现listFiles中有个SmbFileFilter参数,意味着它支持过滤器,而这个过滤器是个interface接口,说明我们可以自定义实现该接口。
代码如下(该方法可以复制进SMBUtils类中):
/**
* 大于某个时间点的文件
*
* @param smbConfig
* @param time 时间节点
* @return
*/
public static List<SmbFile> filesLaterThenTime(SMBConfig smbConfig, long time) {
List<SmbFile> res = new ArrayList<>();
try {
UniAddress ua = UniAddress.getByName(smbConfig.getSmbIp());
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(smbConfig.getSmbIp(),
smbConfig.getSmbUser(), smbConfig.getSmbPwd());
SmbSession.logon(ua, auth);
SmbFile sf = new SmbFile("smb://" + smbConfig.getSmbIp() + "/test" + "/", auth);
sf.connect();
SmbFile sffolder = new SmbFile("smb://" + smbConfig.getSmbIp() + "/test" + "/", auth);
// 设置时间
SmbLaterThenTimeFileFilter.time.set(time);
if (sffolder.exists()) {
// 传入自定义过滤器
SmbFile[] smbFiles = sffolder.listFiles(new SmbLaterThenTimeFileFilter());
for (SmbFile smbFile : smbFiles) {
res.add(smbFile);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
熟悉filter的同学 看到accept方法 就应该知道怎么回事了
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileFilter;
import java.util.concurrent.atomic.AtomicLong;
/**
* 过滤器:最新修改时间大于某个时间点
*
* @author qkj
*/
public class SmbLaterThenTimeFileFilter implements SmbFileFilter {
public static AtomicLong time = new AtomicLong();
@Override
public boolean accept(SmbFile smbFile) throws SmbException {
// 最后修改时间大于time(也可以替换成自己要的逻辑)
return smbFile.getLastModified() > time.get();
}
}
smb注意事项
smb1.0和2.0方式有差异 依赖也不同,需要确认smb1.0在windows服务器中是否开启。
win10/win11中 在启用或关闭windows功能中开启smb1.0 :
windows server中 在powerShell用命令开启smb1.0:
Set-SmbServerConfiguration -EnableSMB1Protocol $true
下面是操作系统的smb默认协议对照图(供参考):
如果windows服务器没有账号密码,需要先设置;
如果共享功能没有开启 需要先开启
java smb2.0/3.0传输(smbj方式)
smbj包人气相对较高,目前也没宣布停止维护,但是博主询问过smbj的作者 得到回复是无法从api层面根据创建、修改等时间去过滤,如果没有时间等条件过滤需求(或者允许将文件列表查出 在本地通过代码去过滤) 可以考虑用该方式
2.0/3.0的上传、下载、列表功能,代码也简单给出:
依赖:
<!-- smb2.0/3.0 -->
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>smbj</artifactId>
<!-- 可以在github中 自行查看当前最新版本-->
<version>0.11.5</version>
</dependency>
import com.hierynomus.msdtyp.AccessMask;
import com.hierynomus.msfscc.FileAttributes;
import com.hierynomus.msfscc.fileinformation.FileIdBothDirectoryInformation;
import com.hierynomus.mssmb2.SMB2CreateDisposition;
import com.hierynomus.mssmb2.SMB2CreateOptions;
import com.hierynomus.mssmb2.SMB2ShareAccess;
import com.hierynomus.smbj.SMBClient;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@Slf4j
public class SMB2Utils {
/**
* 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) {
File shareFile = null;
InputStream shareIs = null;
ByteArrayOutputStream shareBaos = null;
SMBClient client = null;
Session session = null;
DiskShare diskShare = null;
Connection connection = null;
AuthenticationContext authenticationContext = null;
try {
client = new SMBClient();
connection = client.connect(remoteIp);
// 创建连接会话.
authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
session = connection.authenticate(authenticationContext);
// 操作共享文件.
diskShare = (DiskShare) session.connectShare(remoteUrl);
// 获取文件流.上传文件.
shareFile = diskShare.openFile(filename, EnumSet.of(AccessMask.GENERIC_READ),
EnumSet.of(FileAttributes.FILE_ATTRIBUTE_NORMAL), SMB2ShareAccess.ALL,
SMB2CreateDisposition.FILE_OPEN_IF, EnumSet.noneOf(SMB2CreateOptions.class));
shareIs = shareFile.getInputStream();
// 读取文件并下载.
shareBaos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (shareIs.read(buffer) != -1) {
shareBaos.write(buffer);
buffer = new byte[1024];
}
return shareBaos.toByteArray();
} catch (Exception ex) {
log.error(ex.getMessage());
return null;
} finally {
try {
if (null != shareIs) {
shareIs.close();
}
if (null != diskShare) {
diskShare.close();
}
if (null != session) {
session.close();
}
if (null != client) {
client.close();
}
} catch (IOException ex) {
log.error(ex.getMessage());
return null;
}
}
}
/**
* 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) {
File shareFile = null;
OutputStream shareOs = null;
SMBClient client = null;
Session session = null;
DiskShare diskShare = null;
Connection connection = null;
AuthenticationContext authenticationContext = null;
try {
client = new SMBClient();
connection = client.connect(remoteIp);
// 创建连接会话.
authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
session = connection.authenticate(authenticationContext);
// 操作共享文件.
diskShare = (DiskShare) session.connectShare(remoteUrl);
// 获取文件流.上传文件.
shareFile = diskShare.openFile(filename, EnumSet.of(AccessMask.GENERIC_WRITE),
EnumSet.of(FileAttributes.FILE_ATTRIBUTE_NORMAL), SMB2ShareAccess.ALL,
SMB2CreateDisposition.FILE_OPEN_IF, EnumSet.noneOf(SMB2CreateOptions.class));
shareOs = shareFile.getOutputStream();
shareOs.write(fileBytes);
return true;
} catch (Exception ex) {
log.error(ex.getMessage());
return false;
} finally {
try {
if (null != shareOs) {
shareOs.close();
}
if (null != shareFile) {
shareFile.close();
}
if (null != diskShare) {
diskShare.close();
}
if (null != session) {
session.close();
}
if (null != client) {
client.close();
}
} catch (IOException ex) {
log.error(ex.getMessage());
return false;
}
}
}
/**
* 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) {
SMBClient client = null;
Session session = null;
DiskShare diskShare = null;
Connection connection = null;
AuthenticationContext authenticationContext = null;
try {
client = new SMBClient();
connection = client.connect(remoteIp);
// 创建连接会话.
authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
session = connection.authenticate(authenticationContext);
// 操作共享文件.
diskShare = (DiskShare) session.connectShare(remoteUrl);
// 获取文件流.上传文件.
diskShare.rm(filename);
return true;
} catch (Exception ex) {
log.error(ex.getMessage());
return false;
} finally {
try {
if (null != diskShare) {
diskShare.close();
}
if (null != session) {
session.close();
}
if (null != client) {
client.close();
}
} catch (IOException ex) {
log.error(ex.getMessage());
return false;
}
}
}
/**
* SMB查看文件列表.
*
* @param remoteIp 远程IP.
* @param remoteUrl 远程地址.
* @param username 远程用户.
* @param password 远程密码.
* @return 结果.
*/
public static List<FileIdBothDirectoryInformation> smbList(String remoteIp, String remoteUrl, String username, String password) {
SMBClient client = null;
Session session = null;
DiskShare diskShare = null;
Connection connection = null;
AuthenticationContext authenticationContext = null;
try {
client = new SMBClient();
connection = client.connect(remoteIp);
// 创建连接会话.
authenticationContext = new AuthenticationContext(username, password.toCharArray(), null);
session = connection.authenticate(authenticationContext);
// 操作共享文件.
diskShare = (DiskShare) session.connectShare(remoteUrl);
// 查看文件列表(在已知remoteUrl的情况下 list的path参数为 "/" )
List<FileIdBothDirectoryInformation> list = diskShare.list("/");
return list;
} catch (Exception ex) {
log.error(ex.getMessage());
} finally {
try {
if (null != diskShare) {
diskShare.close();
}
if (null != session) {
session.close();
}
if (null != client) {
client.close();
}
} catch (IOException ex) {
log.error(ex.getMessage());
}
}
return new ArrayList<>();
}
// 测试
public static void main(String[] args) throws Exception {
String remoteIp = "192.168.1.1";
String remoteUrl = "test";
String username = "xxx";
String password = "123456";
String filename = "demo.png";
byte[] fileBytes = FileUtils.readFileToByteArray(new java.io.File("pom.xml"));
// 上传文件.
SMB2Utils.smbUpload(remoteIp, remoteUrl, username, password, filename, fileBytes);
// 下载文件.
byte[] downBytes = SMB2Utils.smbDownload(remoteIp, remoteUrl, username, password, filename);
System.out.println(downBytes.length);
// 删除文件.
SMB2Utils.smbDelete(remoteIp, remoteUrl, username, password, filename);
}
}
java smb2.0传输(jcifs-ng方式)
jcifs-ng方式,和jcifs类似,也可以针对时间过滤 大体和之前没有区别,但缺点是jcifs-ng 已经宣布停止维护了 (smbj也是老项目了 其实改动概率都不是很大), 而且网上jcifs-ng资料很少,遇到问题需要自己有一定能力去解决
默认支持1.0-2.0,如果3.0 可能需要自行调整。
依赖:(需要将上文smb1中的jcifs依赖排除)
<!-- smb1.0/2.0/部分3.0 方式二 -->
<dependency>
<groupId>eu.agno3.jcifs</groupId>
<artifactId>jcifs-ng</artifactId>
<version>2.1.9</version>
</dependency>
代码:
import com.qiuhuanhen.springroot.domain.model.SMBConfig;
import jcifs.CIFSContext;
import jcifs.context.SingletonContext;
import jcifs.smb.NtlmPasswordAuthenticator;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author qkj
*/
@Slf4j
public class SMB23Utils {
public static void main(String[] args) throws Exception {
CIFSContext context = withNTLMCredentials(SingletonContext.getInstance());
SMBConfig smbConfig = new SMBConfig();
smbConfig.setSmbIp("192.168.1.1");
smbConfig.setSmbPwd(PASSWORD);
smbConfig.setSmbUser(USERNAME);
smbConfig.setSmbPath("/test");
smbConfig.setSmbFile("1.txt");
smbConfig.setOs(new FileOutputStream("test1.txt"));
// smbConfig.setIs(new FileInputStream("1.txt"));
List<SmbFile> files = filesLaterThenTime(smbConfig,context,1676962027799L);
retrieveFile(smbConfig, context);
// storeFile(smbConfig, context);
context.close();
}
/**
* 列出所有文件
* @param smbConfig
* @param context
* @return
* @throws Exception
*/
public static List<SmbFile> files(SMBConfig smbConfig,CIFSContext context) throws Exception {
List<SmbFile> res = new ArrayList<>();
SmbFile sffolder = new SmbFile("smb://" + smbConfig.getSmbIp() + smbConfig.getSmbPath() + "/", context);
SmbFile[] smbFiles = sffolder.listFiles();
for (SmbFile smbFile : smbFiles) {
res.add(smbFile);
}
return res;
}
/**
* 列出某个修改时间点后的文件
* @param smbConfig
* @param context
* @param time
* @return
* @throws Exception
*/
public static List<SmbFile> filesLaterThenTime(SMBConfig smbConfig,CIFSContext context, long time) throws Exception {
List<SmbFile> res = new ArrayList<>();
SmbFile sffolder = new SmbFile("smb://" + smbConfig.getSmbIp() + smbConfig.getSmbPath() + "/", context);
SmbLaterThenTimeFileFilter.time.set(time);
SmbFile[] smbFiles = sffolder.listFiles(new SmbLaterThenTimeFileFilter());
for (SmbFile smbFile : smbFiles) {
res.add(smbFile);
}
return res;
}
/**
* 往samba上传文件
*
* @param smbConfig
* @return
*/
public static boolean storeFile(SMBConfig smbConfig, CIFSContext context) {
return storeFile(smbConfig.getSmbIp(), context, smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getIs());
}
/**
* 从samba下载文件
*
* @param smbConfig
* @return
*/
public static boolean retrieveFile(SMBConfig smbConfig, CIFSContext context) {
return retrieveFile(smbConfig.getSmbIp(), context, smbConfig.getSmbPath(), smbConfig.getSmbFile(), smbConfig.getOs());
}
/**
* 往samba上传文件
*
* @param url 服务器IP地址
* @param storePath 服务器文件存储路径
* @param fileName 服务器文件存储名称
* @param is 文件输入流
* @return true:上传成功
* <p>
* false:上传失败
*/
public static boolean storeFile(String url, CIFSContext context, String storePath, String fileName, InputStream is) {
BufferedInputStream bf = null;
OutputStream os = null;
boolean result = false;
SmbFile sf = null;
try {
SmbFile sffolder = new SmbFile("smb://" + url + storePath, context);
if (!sffolder.exists()) {
sffolder.mkdirs();
}
sf = new SmbFile("smb://" + url + storePath + "/" + fileName, context);
sf.connect();
bf = new BufferedInputStream(is);
os = sf.getOutputStream();
byte[] bt = new byte[1024];
int n = bf.read(bt);
while (n != -1) {
os.write(bt, 0, n);
os.flush();
n = bf.read(bt);
}
result = true;
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
try {
if (bf != null)
bf.close();
if (os != null)
os.close();
if (null != sf)
sf.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return result;
}
/**
* 从samba下载文件
*
* @param url 服务器IP地址
* @param remotePath 服务器文件存储路径
* @param fileName 服务器文件存储名称
* @param os 文件输出流
* @return true:下载成功
* <p>
* false:下载失败
*/
public static boolean retrieveFile(String url, CIFSContext context, String remotePath, String fileName, OutputStream os) {
boolean result = false;
BufferedInputStream bf = null;
OutputStream bos = new BufferedOutputStream(os);
InputStream sf = null;
SmbFile file = null;
try {
file = new SmbFile("smb://" + url + remotePath + "/" + fileName, context);
file.connect();
sf = new SmbFileInputStream(file);
byte[] bt = new byte[1024];
int n = sf.read(bt);
while (n != -1) {
os.write(bt, 0, n);
os.flush();
n = sf.read(bt);
}
result = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != sf)
sf.close();
if (bos != null) {
bos.close();
}
if (os != null)
os.flush();
os.close();
if (bf != null)
bf.close();
if (null!=file)
file.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return result;
}
public static CIFSContext withNTLMCredentials(CIFSContext ctx) {
return ctx.withCredentials(new NtlmPasswordAuthenticator(
USERNAME, PASSWORD));
}
}
model类代码:
/**
* smb传输 model类
* SMB地址写法:smb://用户名:密码@远程ip/地址/(或有无文件名)(注:如只获取文件夹后缀一定有“/”)
* <p>
*/
@Data
@EqualsAndHashCode
public class SMBConfig implements Serializable {
/**
* 服务器ip
*/
private String smbIp;
/**
* 服务器账号
*/
private String smbUser;
/**
* 服务器密码
*/
private String smbPwd;
/**
* 文件路径 在共享文件夹中的路径 (不带计算机名称)
*/
private String smbPath;
/**
* 文件名
*/
private String smbFile;
/**
* 输入流
*/
private InputStream is;
/**
* 输出流
*/
private OutputStream os;
public SMBConfig() {
}
}
这个代码调用下载方法 在运行结束后会报debug级别的异常以及java.net.SocketException: Socket closed,目前没有发现会影响使用,debug级别的错误 End of file 作者在issue区说没什么问题 它就是代表结束了 ,socket closed或许socket机制本身如此