工作需求记录:创建一个用于上传文件到服务器的SFTP工具类(使用JSch实现)

中午发来一个需求,创建一个Sftp传输文件到服务器的工具类。
首先需要解决的问题是,什么是Sftp?在使用Shell连接远程服务器的时候,诸如XShell或者我使用的Tabby Terminal都使用了SSH连接。
Sftp可以理解为SSH连接的一部分,作为Sftp连接的服务器只允许进行文件的上传和下载的一系列操作,但如果不写额外的方法,就没法知道到底上传是否成功。
在JAVA里要实现Sftp传输文件,最简单的是引入一个叫JSch的包,这里提供了丰富的方法来使用。除此之外,我们熟悉的Hutool也带有Sftp的基础实现,但单靠Hutool传输,
需要创建一个临时文件,再把它删除掉,因为Hutool的upload方法不支持IO流的直接使用,而是接收File类型,这里就涉及了多一步转换,比较麻烦。
所以我们直接在Maven Repository里找到并在pom里引入JSch的包:

<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>

如果是SSH连接的话自然可以在三方软件里用带有GUI的界面轻松上传下载,但我们现在需要自己构建这个流程,所以第一步就是解决创建连接的问题,在创建Session的时候,需要配置服务器的username,host,post和password,这些可以去yml里配置,如果是微服务则需要去Nacos之类的集成中心修改配置并发布。

/**
     * 创建SFTP会话
     */
    private Session createSession() throws JSchException {
        JSch jsch = new JSch();
        Session session = jsch.getSession(username, host, port);
        session.setPassword(password);
        session.setTimeout(timeout);
        session.connect();
        return session;
    }

这里我们new出一个JSch来调用他的getSession方法传入配置来建立连接,并配置对应的属性,比如我在这里配置了sftp连接的超时时间timeout,3000为30秒。
想要像三方软件那样直接操作Linux,还需要创建一个Sftp通道ChannelSftp,这个类也是JSch包里自带的,只需要new出来,调用connect方法即可。
关于这里为什么要强转,这其实是一个父转子,Sftp的具体方法在ChannelSftp类中,但是Session只能提供父类Channel,里面没有ChannelSftp的方法

 /**
     * 创建SFTP通道
     */
    private ChannelSftp createChannelSftp(Session session) throws JSchException {
        ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
        channelSftp.connect();
        return channelSftp;
    }

至此我们已经建立了连接,也获得了操作权,下一步就是操作了。
构建我们真正的upload方法,并用上之前的两个辅助方法:

 public String uploadFile(MultipartFile file, String subFolder, String fileName) {
        Session session = null;
        ChannelSftp channelSftp = null;

        try {
            // 创建SFTP连接
            session = createSession();
            channelSftp = createChannelSftp(session);

            // 构建上传路径
            String remotePath = buildRemotePath(subFolder);

            // 检查远程目录是否存在
            createRemoteDirectoryIfNotExists(channelSftp, remotePath);

            // 使用传入的文件名
            if (StrUtil.isBlank(fileName)) {
                throw new RuntimeException("文件名不能为空");
            }
            String fullRemotePath = remotePath + "/" + fileName;

            // 使用InputStream上传
            InputStream inputStream = file.getInputStream();
            channelSftp.put(inputStream, fullRemotePath);
            inputStream.close();
            return fullRemotePath;

        } catch (Exception e) {
            log.error("SFTP文件上传失败: {}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        } finally {
            // 关闭连接
            closeConnection(channelSftp, session);
        }

我这里多了一步检查服务器是否存在上传路径的步骤,如果确定是有的话,就不需要再多做判断了。
至此,用于上传的SftpUtil就构建完成了,可以构建接口调用,或者用于前置后置必须业务都可以,这种业务场景一般都是需要前置,也就是必须上传文件后才能继续流程,在自己的方法里调用并判断一下,就可以了。除了上传以外,SftpUtil还支持下载等很多其他功能,可以自己查看一下JSch包里封装的方法探索和实现。

posted @ 2025-08-28 15:08  ZEROdokiame  阅读(49)  评论(0)    收藏  举报