Java 项目如何连接并使用 SFTP 服务的示例详解

文章目录

1、SFTP介绍

SFTP(Secure File Transfer Protocol)是一种安全的文件传输协议,是SSH(Secure Shell)协议的一个子协议,设计用于加密和保护文件传输的安全性。SSH本身是一种网络协议,用于在不安全的网络中提供安全的远程登录和其他安全网络服务。SFTP则在此基础上,专注于文件的安全传输。

  • 加密传输:SFTP使用加密来保护传输的数据,包括文件内容和认证信息。
  • 身份验证:SFTP使用SSH身份验证机制来验证用户身份。用户通常需要提供用户名和密码,或者使用SSH密钥对进行身份验证。
  • 文件和目录操作:SFTP支持文件和目录的上传、下载、删除、重命名和创建等操作。
  • 跨平台兼容性:SFTP是一个跨平台协议,可以在各种操作系统上使用,包括Linux、Unix、Windows等。
  • 端到端数据完整性:SFTP确保传输的文件在源和目标之间的完整性,防止数据在传输过程中被篡改或损坏。
  • 可扩展性:SFTP可以与其他协议和安全机制一起使用,以增强其功能。例如,它可以与公钥基础设施(PKI)一起使用以实现更高级的安全性。

SFTP通常用于许多场景,包括远程服务器维护、备份、文件共享和在不同计算机之间传输敏感数据。由于它提供了强大的安全性,因此特别适用于传输金融账户、公司文件和政府数据等敏感信息。

  • 端口:SFTP的默认端口与SSH相同,为22。这意味着只要sshd服务器启动了,SFTP就可使用,不需要额外安装。
  • 守护进程:SFTP本身没有单独的守护进程,它必须使用SSHD守护进程(端口号默认是22)来完成相应的连接操作。
  • 配置:SFTP的配置通常与SSH配置相关。例如,可以通过修改SSH配置文件(如sshd_config)来启用或禁用SFTP功能,以及设置相关的访问权限和安全策略。

SFTP结合了SSH的安全性和文件传输的便捷性,成为许多组织和个人在传输敏感数据时的首选协议。

2、pom依赖

SFTP需要第三方依赖进行连接。

xml 复制代码
<dependency>
	<groupId>com.jcraft</groupId>
	<artifactId>jsch</artifactId>
	<version>0.1.55</version>
</dependency>

3、SFTPUtil

java 复制代码
package com.wen.util;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Date;
import java.util.Properties;
import java.util.Vector;

/**
 * @author : rjw
 * @date : 2024-10-14
 */
public class SFTPUtil {
  
    private static final Logger logger = LoggerFactory.getLogger(SFTPUtil.class);

    private Session session;

    private ChannelSftp channelSftp;

	/**
	* 登录
	*/
    public boolean login(String hostname, int port, String username, String password) {
        try {
            JSch jSch = new JSch();
            // 根据用户名,IP地址获取一个session对象。
            session = jSch.getSession(username, hostname, port);
            session.setPassword(password);
            // 避免第一次连接时需要输入yes确认
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.connect();
            logger.info("成功连接服务器");
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();
            logger.info("成功连接服务器");
            return true;
        } catch (JSchException e) {
            logger.error("SFTPUtil login file error:", e);
        }
        return false;
    }
	
	/**
	* 退出
	*/
    public void logout() {
        if (channelSftp != null && channelSftp.isConnected()) {
            try {
                channelSftp.disconnect();
                logger.info("成功退出 SFTP 服务器");
            } catch (Exception e) {
                logger.error("退出SFTP服务器异常: ", e);
            } finally {
                try {
                    channelSftp.disconnect(); // 关闭FTP服务器的连接
                } catch (Exception e) {
                    logger.error("关闭SFTP服务器的连接异常: ", e);
                }
            }
        }
        if (session != null && session.isConnected()) {
            try {
                session.disconnect();
                logger.info("成功退出 session 会话");
            } catch (Exception e) {
                logger.error("退出 session 会话异常: ", e);
            } finally {
                try {
                    session.disconnect(); // 关闭FTP服务器的连接
                } catch (Exception e) {
                    logger.error("关闭 session 会话的连接异常: ", e);
                }
            }
        }
    }

	/**
	* 判断是否连接
	*/
    public boolean isConnected() {
        return channelSftp != null && channelSftp.isConnected();
    }

	/**
     * 上传文件
     * 采用默认的传输模式:OVERWRITE
     *
     * @param src 输入流(文件)
     * @param dst 上传路径
     * @param fileName 上传文件名
     * @throws SftpException
     */
    public boolean upLoadFile(InputStream src, String dst, String fileName) throws SftpException {
        boolean isSuccess = false;
        try {
            if (createDir(dst)) {
                channelSftp.put(src, fileName);
                isSuccess = true;
            }
        } catch (SftpException e) {
            logger.error(fileName + "文件上传异常", e);
        }
        return isSuccess;
    }

    /**
     * 创建一个文件目录
     *
     * @param createPath 路径
     * @return
     */
    public boolean createDir(String createPath) {
        boolean isSuccess = false;
        try {
            if (isDirExist(createPath)) {
                channelSftp.cd(createPath);
                return true;
            }
            String[] pathArray = createPath.split("/");
            StringBuilder filePath = new StringBuilder("/");
            for (String path : pathArray) {
                if (path.isEmpty()) {
                    continue;
                }
                filePath.append(path).append("/");
                if (isDirExist(filePath.toString())) {
                    channelSftp.cd(filePath.toString());
                } else {
                    // 建立目录
                    channelSftp.mkdir(filePath.toString());
                    // 进入并设置为当前目录
                    channelSftp.cd(filePath.toString());
                }
            }
            channelSftp.cd(createPath);
            isSuccess = true;
        } catch (SftpException e) {
            logger.error("目录创建异常!", e);
        }
        return isSuccess;
    }

	/**
     * 判断目录是否存在
     *
     * @param directory 路径
     * @return
     */
    public boolean isDirExist(String directory) {
        boolean isSuccess = false;
        try {
            SftpATTRS sftpATTRS = channelSftp.lstat(directory);
            isSuccess = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
        	logger.info("SFTPUtil path has error:{}", directory);
            logger.error("SFTPUtil path has error: ", e);
            if (e.getMessage().equalsIgnoreCase("no such file")) {
                isSuccess = false;
            }
        }
        return isSuccess;
    }

    /**
     * 重命名指定文件或目录
     */
    public boolean rename(String oldPath, String newPath) {
        boolean isSuccess = false;
        try {
            channelSftp.rename(oldPath, newPath);
            isSuccess = true;
        } catch (SftpException e) {
            logger.error("重命名指定文件或目录异常", e);
        }
        return isSuccess;
    }
    
    /**
     * 列出指定目录下的所有文件和子目录。
     */
    public Vector ls(String path) {
        try {
            Vector vector = channelSftp.ls(path);
            return vector;
        } catch (SftpException e) {
            logger.error("列出指定目录下的所有文件和子目录。", e);
        }
        return null;
    }

	/**
     * 删除文件
     */
	public boolean deleteFile(String directory, String deleteFile) {
        boolean isSuccess = false;
        try {
            channelSftp.cd(directory);
            channelSftp.rm(deleteFile);
            isSuccess = true;
        } catch (SftpException e) {
            logger.error("删除文件失败", e);
        }
        return isSuccess;
    }

    /**
     * 下载文件
     *
     * @param directory 下载目录
     * @param downloadFile 下载的文件
     * @param saveFile 下载到本地路径
     */
    public boolean download(String directory, String downloadFile, String saveFile) {
        boolean isSuccess = false;
        try {
            channelSftp.cd(directory);
            File file = new File(saveFile);
            channelSftp.get(downloadFile, new FileOutputStream(file));
            isSuccess = true;
        } catch (SftpException e) {
            logger.error("下载文件失败", e);
        } catch (FileNotFoundException e) {
            logger.error("下载文件失败", e);
        }
        return isSuccess;
    }

	/**
     * 输出指定文件流
     */
	public InputStream getFile(String path) {
		try {
			InputStream inputStream = channelSftp.get(path);
			return inputStream;
		} catch (SftpException e) {
            throw new RuntimeException(e);
        }
	}

    /**
     * 下载文件,新
     */
    public InputStream downloadFile(String remoteFileName, String remoteFilePath) {
        InputStream input = null;
        try {
            if (!isDirExist(remoteFilePath)) {
                logger.info("SFTPUtil not changeWorkingDirectory.filename:{},Path:{}", remoteFileName, remoteFilePath);
                logout();
                return input;
            }
            logger.info("SFTPUtil Info filename:{},Path:{}", remoteFileName, remoteFilePath);
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            input = new ByteArrayInputStream(outputStream.toByteArray());
            outputStream.close();
            logger.info("file download. filename:{},Path:{}", remoteFileName, remoteFilePath);
        } catch (Exception e) {
            logger.error("SFTPUtil download file error:", e);
            logout();
        }
        return input;
    }

	/**
     * 验证sftp文件是否有效(判断文件属性的日期)
     */
    public String validateSourceData(String fileName, String remoteFilePath, Date nowTime) throws IOException, SftpException {
        Date temp = null;
        if (!isDirExist(remoteFilePath)) {
            logout();
            return "尝试切换SFTP路径失败, 文件路径: " + remoteFilePath + fileName;
        }
        Vector vector = channelSftp.ls(remoteFilePath);
        for (int i = 0; i < vector.capacity(); i++) {
            ChannelSftp.LsEntry data = (ChannelSftp.LsEntry) vector.get(i);
            if (data.getFilename().equalsIgnoreCase(fileName)) {
                temp = new Date(data.getAttrs().getMTime() * 1000L);
                //logger.info("时间: timeStamp:{},nowTime:{}",temp,nowTime);
                if (temp.getTime() == nowTime.getTime()) {
                    return "SFTP文件没有更新";
                }
                nowTime.setTime(temp.getTime());
                break;
            }
        }
        //logout();
        return null;
    }


	/**
     * IP
     */
    public String getSFTPHost() {
        return session.getHost();
    }

	/**
     * 端口
     */
    public int getSFTPPort() {
        return session.getPort();
    }
}

4、测试

java 复制代码
public class Test {

	private static SFTPUtil sftpUtil = new SFTPUtil();

	public static String convertInputStreamToString(InputStream inputStream) throws IOException{
        StringBuilder stringBuilder = new StringBuilder();
        try{
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            String line;
            while ((line = bufferedReader.readLine()) != null){
                System.out.println(line);
                stringBuilder.append(line).append("\n");
            }
        }catch (Exception e){
            System.out.println(e);
        }
        if(stringBuilder.length() > 0 && stringBuilder.charAt(stringBuilder.length() - 1) == '\n'){
            stringBuilder.setLength(stringBuilder.length() - 1);
        }
        return stringBuilder.toString();
    }

    public static void main(String[] args) {
        boolean connected = sftpUtil.isConnected();
        if (!connected) {
            System.out.println("连接SFTP");
            // 折里可以采用配置文件 @Value 注解
            connected = sftpUtil.login("10.26.73.163", 2222, "username", "password");
        }
        if (connected) {
            System.out.println("连接成功");
            System.out.println(sftpUtil.getSFTPHost() + " : " + sftpUtil.getSFTPPort());
            InputStream inputStream = sftpUtil.getFile("/rjw/wind.txt");
            String s = convertInputStreamToString(inputStream);
            System.out.println(s);
            File file = new File("C:\\Users\\wen\\Desktop\\sun.txt");
            InputStream inputStream1 = Files.newInputStream(file.toPath());
            boolean dir = sftpUtil.upLoadFile(inputStream1, "/rjw", "big.txt");
            System.out.println("添加文件成功: " + dir);
            sftpUtil.logout();
        }
    }
}

5、测试结果

相关推荐
小白学大数据19 小时前
实战:Python爬虫如何模拟登录与维持会话状态
开发语言·爬虫·python
一念&19 小时前
每日一个C语言知识:C 结构体
c语言·开发语言
Chen-Edward20 小时前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
锦***林20 小时前
用 Python 写一个自动化办公小助手
开发语言·python·自动化
陈小桔20 小时前
idea中重新加载所有maven项目失败,但maven compile成功
java·maven
小学鸡!20 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
xiaogg367821 小时前
阿里云k8s1.33部署yaml和dockerfile配置文件
java·linux·kubernetes
逆光的July21 小时前
Hikari连接池
java
微风粼粼21 小时前
eclipse 导入javaweb项目,以及配置教程(傻瓜式教学)
java·ide·eclipse
番茄Salad21 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud