public class SftpFileUploader {
private List<String> localSourceDirectories;
private String remoteDestinationDirectory;
private String username;
private String hostname;
private int port;
private String password;
private long uploadIntervalMinutes;
private boolean enableCleanup;
private long cleanupIntervalMinutes;
private Set<String> uploadedFiles;
private long fileAgeThresholdMillis;
public SftpFileUploader(List<String> localSourceDirectories, String remoteDestinationDirectory,
String username, String hostname, int port, String password,
long uploadIntervalMinutes, boolean enableCleanup, long cleanupIntervalMinutes, long fileAgeThresholdMinutes) {
this.localSourceDirectories = localSourceDirectories;
this.remoteDestinationDirectory = remoteDestinationDirectory;
this.username = username;
this.hostname = hostname;
this.port = port;
this.password = password;
this.uploadIntervalMinutes = uploadIntervalMinutes;
this.enableCleanup = enableCleanup;
this.cleanupIntervalMinutes = cleanupIntervalMinutes;
this.uploadedFiles = new HashSet<>();
this.fileAgeThresholdMillis = fileAgeThresholdMinutes * 24 * 60 * 60 * 1000 ;
}
/**
* 创建了一个周期性任务,用于上传文件,可以根据uploadIntervalMinutes参数的设置来控制上传频率。
*/
public void startPeriodicUploadAndCleanup() {
// lambda表达式创建 uploadFiles任务
Runnable uploadTask = this::uploadFiles;
uploadTask.run();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
//代码创建了一个大小为1的线程池,用于执行定时任务。代码创建了一个大小为1的线程池,用于执行定时任务。
executor.scheduleAtFixedRate(uploadTask, uploadIntervalMinutes, uploadIntervalMinutes, TimeUnit.MINUTES);
//uploadTask会被以固定的频率执行,这个频率由uploadIntervalMinutes参数确定,单位是分钟。这意味着每隔uploadIntervalMinutes分钟,都会执行一次文件上传操作。
/**
*
* scheduleAtFixedRate方法接受四个参数:
*
* uploadTask: 要执行的任务,这里是this::uploadFiles。
* initialDelay: 首次执行任务的延迟时间,设为0表示立即开始执行。
* period: 每次执行任务的时间间隔。
* unit: 时间间隔的单位,这里是TimeUnit.MINUTES,表示时间间隔是以分钟为单位。
*
* */
if (enableCleanup) {
Runnable cleanupTask = this::clearFiles;
executor.scheduleAtFixedRate(cleanupTask, cleanupIntervalMinutes, cleanupIntervalMinutes, TimeUnit.DAYS);
}
}
/**
* 通过SSH协议连接到远程服务器,使用SFTP通道实现了将本地文件上传到远程服务器的操作。
* 在上传过程中,它还会创建远程子目录并递归地传输文件
*/
private void uploadFiles() {
for (String localSourceDirectory : localSourceDirectories) {
JSch jsch = new JSch(); //用于处理安全的通信连接,主要用于SSH协议。
Session session = null; //声明了一个Session对象,用于表示与远程服务器的连接会话。
ChannelSftp channelSftp = null; //声明了一个ChannelSftp对象,表示SFTP(SSH File Transfer Protocol)通道,用于进行文件传输。
try {
session = jsch.getSession(username, hostname, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
channelSftp = (ChannelSftp) session.openChannel("sftp");
//:配置会话以禁用严格的主机密钥检查,这可以在某些情况下简化连接建立过程。
channelSftp.connect();
File sourceDirectory = new File(localSourceDirectory);
//创建一个File对象,表示本地源目录。
String remoteSubDirectory = remoteDestinationDirectory + "/" + sourceDirectory.getName();
//建一个远程子目录的路径,将文件上传到目标远程目录下的与本地源目录同名的子目录。
createRemoteDirectories(channelSftp, sourceDirectory, remoteSubDirectory);
//用于在远程服务器上递归地创建目录结构。
uploadDirectory(channelSftp, sourceDirectory, remoteSubDirectory);
} catch (JSchException | SftpException e) {
System.err.println("Error uploading files from: " + localSourceDirectory);
e.printStackTrace();
} finally {
if (channelSftp != null && channelSftp.isConnected()) {
channelSftp.disconnect(); //断开连接
}
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
}
private void clearFiles() {
if (!enableCleanup) {//检查是否启用了清理操作
System.out.println("Clear files not enabled. Skipping.");
return;
}
System.out.println("Clearing files from remote directories...");
JSch jsch = new JSch();
Session session = null;
ChannelSftp channelSftp = null;
try {
session = jsch.getSession(username, hostname, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
for (String localSourceDirectory : localSourceDirectories) {
File sourceDirectory = new File(localSourceDirectory);
String remoteSubDirectory = remoteDestinationDirectory + "/" + sourceDirectory.getName();
clearRemoteDirectory(channelSftp, remoteSubDirectory);//清理远程目录
}
} catch (JSchException | SftpException e) {
System.err.println("Error clearing remote files: " + e.getMessage());
e.printStackTrace();
} finally {
if (channelSftp != null && channelSftp.isConnected()) {
channelSftp.disconnect();
}
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
private void clearRemoteDirectory(ChannelSftp channelSftp, String remoteDirectory) throws SftpException {
Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(remoteDirectory);
//使用Vector的原因可能是为了处理文件列表的变化,并且在需要时可以动态添加和移除元素
if (entries != null) {
for (ChannelSftp.LsEntry entry : entries) {
String fileName = entry.getFilename();
if (!fileName.equals(".") && !fileName.equals("..")) {
//排除掉当前目录(.)和上级目录(..)的情况
String filePath = remoteDirectory + "/" + fileName;
//构建文件的完整路径。
if (entry.getAttrs().isDir()) {
clearRemoteDirectory(channelSftp, filePath);
} else {
//检查当前条目是否是一个目录。如果是目录,进入一个分支,如果是文件,进入另一个分支。
SftpATTRS attrs = entry.getAttrs();
long modificationTime = attrs.getMTime() * 1000L; // Convert to milliseconds
// 获取文件的修改时间并转换为毫秒。SFTP属性中的修改时间以秒为单位,乘以1000转换为毫秒。
if (enableCleanup) {
long currentTime = System.currentTimeMillis();
long fileAge = currentTime - modificationTime;
//计算文件的年龄
if (fileAge > fileAgeThresholdMillis) {
channelSftp.rm(filePath);
//检查文件的年龄是否超过了阈值。如果超过阈值,进入一个分支,如果没有超过,进入另一个分支
System.out.println("File '" + filePath + "' deleted from remote directory.");
} else {
System.out.println("File '" + filePath + "' retained in remote directory.");
}
}
}
}
}
}
}
private void createRemoteDirectories(ChannelSftp channelSftp, File localDirectory, String remoteDirectory) throws SftpException {
String[] directories = remoteDirectory.split("/");
//将远程目录路径按照斜杠(/)进行分割,得到一个字符串数组,表示每个目录名称。
StringBuilder dirPath = new StringBuilder();
//创建一个StringBuilder对象,用于构建远程目录路径。
for (String dir : directories) {
if (dir.length() > 0) {
//检查目录名称是否非空,以排除空白项。
dirPath.append("/").append(dir);
//将当前目录名称追加到dirPath中,构建出当前的远程目录路径。
try {
channelSftp.cd(dirPath.toString());
//尝试切换到远程目录。如果目录不存在,将会抛出SftpException异常。
} catch (SftpException e) {
channelSftp.mkdir(dirPath.toString());
//在远程服务器上创建目录
channelSftp.cd(dirPath.toString());
//切换到刚刚创建的远程目录。
}
}
}
File[] files = localDirectory.listFiles();
//获取本地目录下的所有文件和子目录。
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
//检查当前项是否是一个目录。
String remoteSubDirectory = remoteDirectory + "/" + file.getName();
//构建一个远程子目录的路径。
createRemoteDirectories(channelSftp, file, remoteSubDirectory);
}
}
}
}
/**
* 递归地上传本地目录中的文件和子目录到远程服务器的指定目录
*/
private void uploadDirectory(ChannelSftp channelSftp, File localDirectory, String remoteDirectory) throws SftpException {
channelSftp.cd(remoteDirectory);
File[] files = localDirectory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
uploadDirectory(channelSftp, file, remoteDirectory + "/" + file.getName());
//递归调用uploadDirectory方法,将当前本地目录的子目录上传到远程服务器的子目录
} else if (file.isFile()) {
//如果当前项是一个文件,则进入这个分支。
if (!uploadedFiles.contains(file.getAbsolutePath())) {
//检查当前文件是否已经被上传,以避免重复上传。
uploadSingleFile(channelSftp, file, remoteDirectory);
//调用uploadSingleFile,用于将单个文件上传到远程目录。
uploadedFiles.add(file.getAbsolutePath());
//将已上传文件的路径添加到uploadedFiles列表中,以便进行记录和避免重复上传。
}
}
}
}
}
private void uploadSingleFile(ChannelSftp channelSftp, File localFile, String remoteDirectory) throws SftpException {
String remoteFilePath = remoteDirectory + "/" + localFile.getName();
//构建远程文件的完整路径,将远程目录和本地文件名组合在一起。
channelSftp.put(localFile.getAbsolutePath(), remoteFilePath, ChannelSftp.APPEND);
//使用SFTP通道将本地文件上传到远程服务器的指定路径。
// 获取本地文件的绝对路径,remoteFilePath是远程文件路径,ChannelSftp.APPEND表示如果文件已存在,则追加内容。
System.out.println("File '" + localFile.getName() + "' uploaded successfully.");
//如果远程文件已存在,则追加内容
}
}
public static void main(String[] args) {
List localSourceDirectories = new ArrayList<>();
localSourceDirectories.add(“”);
localSourceDirectories.add(“”);
String remoteDestinationDirectory = "";
String username = "";
String hostname = "1";
int port = ;
String password = "";
long uploadIntervalMinutes = 1; // 每5分钟上传一次
long cleanupIntervalMinutes = 1; // 每30分钟清除一次文件
boolean enableCleanup = true; // 是否启用文件清除功能
long fileAgeThresholdMinutes = 30; //分钟为单位设置所需的文件期限阈值
SftpFileUploader fileUploader = new SftpFileUploader(localSourceDirectories, remoteDestinationDirectory,
username, hostname, port, password, uploadIntervalMinutes, enableCleanup, cleanupIntervalMinutes, fileAgeThresholdMinutes);
fileUploader.startPeriodicUploadAndCleanup();
}