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、测试结果

相关推荐
Cao1234567893212 分钟前
扫雷-C语言版
c语言·开发语言
天堂的恶魔94613 分钟前
QT —— 信号和槽(槽函数)
开发语言·qt
牛马baby16 分钟前
Springboot 自动装配原理是什么?SPI 原理又是什么?
java·spring boot·后端
水w19 分钟前
【Python爬虫】详细入门指南
开发语言·爬虫·python·scrapy·beautifulsoup
小小深38 分钟前
了解JVM
java·jvm
Sunlight_77744 分钟前
第五章 SQLite数据库:1、SQLite 基础语法及使用案例
java·linux·服务器·jvm·数据库·tcp/ip·sqlite
JhonKI1 小时前
【从零实现高并发内存池】内存池整体框架设计 及 thread cache实现
java·redis·缓存
何似在人间5751 小时前
SpringAI+DeepSeek大模型应用开发——4 对话机器人
java·机器人·大模型应用开发·spring ai
Susea&1 小时前
数据结构初阶:双向链表
c语言·开发语言·数据结构
pianmian12 小时前
arcgis几何与游标(1)
开发语言·python