在本文中,我们将创建一个 SpringBoot 服务来读取位于 SFTP 服务器上的远程文件并将文件复制到本地目录。
首先在pom.xml文件中添加以下依赖项。
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
创建FileHelper.java
getSession方法将返回 Session 类的对象。JSch 库的会话类用于创建 SSH 会话。通过验证用户身份,它会创建与远程 SSH 服务器的连接。SFTP 服务器的默认端口一般是22。
private Session getSession(String user, String password){
String host = "10.0.0.10";
String port = "22";
JSch jsch = new JSch();
Session session = null;
try {
session = jsch.getSession(user, host,Integer.valueOf(port));
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
LOGGER.info("SFTP Session Created");
} catch (JSchException e) {
/* Handle session or channel disconnect exception
This catch block will handle any exceptions related to session or channel
disconnects that are thrown by the session.connect() or channelSftp.connect() methods */
errorLogger.error("Could Not Create SFTP Session, Possible reasons: {}", e.getMessage());
e.printStackTrace();
}
return session;
}
private ChannelSftp getChannel(Session session){
ChannelSftp channelSftp = null;
if (session != null && session.isConnected()){
try {
channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
LOGGER.info("SFTP Channel Created");
} catch (JSchException e) {
/* Handle SFTP related exception
This catch block will handle any SFTP-related exceptions that may occur during SFTP
operations like listing files (channelSftp.ls()) or changing directories (channelSftp.cd()) */
errorLogger.error("Could Not Create SFTP Channel, Possible reasons: {}", e.getMessage());
e.printStackTrace();
}
}
return channelSftp;
}
getChannel方法将返回 ChannelSftp 类的对象。JSch 库中的 ChannelSftp 类用于使用 SFTP(SSH 文件传输协议)与远程服务器进行交互。它提供了一组方法,允许您通过安全的 SSH 连接执行各种与文件相关的操作,包括上传和下载文件、列出目录、创建目录和删除文件
private Vector<ChannelSftp.LsEntry> getFilesNamesFromSFTP(ChannelSftp channelSftp, String remoteDirectory, String filePrefixAndExtension){
Vector<ChannelSftp.LsEntry> files = new Vector<>();
if(channelSftp != null && channelSftp.isConnected()){
try {
// Change to the remote directory
channelSftp.cd(remoteDirectory);
// List files in the remote directory
files = channelSftp.ls(filePrefixAndExtension);
} catch (SftpException e) {
errorLogger.error("Could Not Create SFTP Channel, Possible reasons: {}", e.getMessage());
e.printStackTrace();
}
}
return files;
}
getFilesNamesFromSFTP方法获取 SFTP 服务器中可用的文件名列表。我们可以使用 ChannelSftp 类的cd函数更改远程服务器的目录,其中我们可以使用ChannelSftp 类的ls函数使用前缀和扩展名过滤文件。
private String downloadFileToLocalDirFromSFTP(ChannelSftp channelSftp, String localDirectory, ChannelSftp.LsEntry file){
if(channelSftp != null && channelSftp.isConnected()){
try {
String localFilePath = localDirectory + File.separator + file.getFilename();
// Download the file from the remote directory to the local directory
channelSftp.get(file.getFilename(), localFilePath);
File downloadedFile = new File(localFilePath);
Date dateModify = new Date(file.getAttrs().getMTime() * 1000L);
downloadedFile.setLastModified(dateModify.getTime());
LOGGER.info("File copied from SFTP, Filename: {}", downloadedFile.getName());
return downloadedFile.getName();
} catch (SftpException e) {
errorLogger.error("Could Not Download File from SFTP Server, File Name: {}, Possible reasons: {}",file.getFilename(), e.getMessage());
e.printStackTrace();
}
}
return null;
}
downloadFileToLocalDirFromSFTP方法使用 ChannelSftp 类的get函数将一个特定文件复制到我们的本地目录,并返回复制的文件名。
private void disconnectChannelAndSession(ChannelSftp channelSftp,Session session){
// Disconnect the SFTP channel and session
if (channelSftp != null && channelSftp.isConnected()) {
channelSftp.disconnect();
LOGGER.info("SFTP Channel Disconnected from SFTP Server");
}
if (session != null && session.isConnected()) {
session.disconnect();
LOGGER.info("SFTP Session Ended from SFTP Server");
}
}
disconnectChannelAndSession将断开SFTP通道并断开与远程SFTP服务器的会话。我们可以在完成从服务器移动或复制文件后调用此函数。
再创建我们的实体对象类FileHistory.java
@Entity
@Table(name = "file_history")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class FileHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique=true)
private String fileName;
private boolean isDownloaded = false;
@Column(updatable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date downloadedAt;
public FileHistory(String fileName, boolean isDownloaded, Date downloadedAt) {
this.fileName = fileName;
this.isDownloaded = isDownloaded;
this.downloadedAt = downloadedAt;
}
}
最后创建一个SftpFileService.java类,实现我们的最终的需求。
@Service
public class SftpFileService{
@Value("${USERNAME}")
private String pdfSFTPUser;
@Value("${PASSWORD}")
private String pdfSFTPPassword;
@Value("${REMOTE_DIR}")
private String remoteDirectory;
@Value("${pdf.file.dir}")
private String localPDFDirectory;
@Autowired
private FileHelper fileHelper;
public void copySftpPdfFiles(){
List<FileHistory> pdfFileList = new ArrayList<FileHistory>();
Session session = this.fileHelper.getSession(pdfSFTPUser, pdfSFTPPassword);
ChannelSftp channel = this.fileHelper.getChannel(session);
Vector<ChannelSftp.LsEntry> files = this.fileHelper.getFilesNamesFromSFTP(channel, remoteDirectory, "*.pdf");
if(files == null || files.isEmpty()){
return;
}
for (ChannelSftp.LsEntry file : files) {
if (file == null || file.getAttrs().isDir() || file.getAttrs().getSize() <= 0 || !this.fileHistoryService.isNewFile(file.getFilename())) {
continue;
}
String downloadedFileName = this.fileHelper.downloadFileToLocalDirFromSFTP(channel, localPDFDirectory, file);
// ** when copy complete, add the name to list
if(downloadedFileName == null){
continue;
}
FileHistory pdfFile = new FileHistory(downloadedFileName,true, new Date());
pdfFileList.add(pdfFile);
}
this.fileHelper.disconnectChannelAndSession(channel,session);
// ** Add list of newly copied files ** //
LOGGER.info("Number of New PDF Files Copied From SFTP: {}",pdfFileList.size());
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)