sftp上传下载

最近在做sftp的上传下载,查询了相关资料,最终选定jsch

jsch的官网看起来有点不舒服,这里就不记了

maven的pom引入,jsch最新版本 0.1.54

 <dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.54</version>
</dependency>

主要包括几个部分:

1、获取sftp通道--ChannelSftp

主要有登录sftp的服务器信息,用户名密码等。然后获取sftp通道并返回。

/**
     * 密码方式登录sftp服务器并获取连接
     *
     * @param host     sftp服务器地址
     * @param port     端口,默认22
     * @param user     sftp服务器登录用户
     * @param password sftp登录用户密码
     * @return
     * @throws JSchException
     */
    public static ChannelSftp getChannelSftp(String host, int port, String user, String password) throws JSchException {
        logger.debug("密码方式登录sftp服务器{},端口{}", host, port);
        JSch jSch = new JSch();
        Session session = jSch.getSession(user, host, port);
        session.setPassword(password);
        //设置首次登录是否需要检查
        session.setConfig("StrictHostKeyChecking", "no");
        //设置连接超时时间 -- 不设置会报session is down的错,暂未查到原因
        session.connect(30000);
        //获取sftp服务器连接
        Channel channel = session.openChannel("sftp");
        channel.connect();
        //返回sftp连接
        return (ChannelSftp) channel;
    }

2、文件下载

jsch提供了一系列的get方法,用于文件下载

/**
     * 从sftp服务器下载当前目录下的所有文件和目录,不在本地创建当前remotePath根目录
     *
     * @param sftp       sftp服务器连接
     * @param remotePath 当前要下载的sftp远程服务器目录
     * @param localPath  当前要下载的文件本地目录
     * @param exceptList 不需要下载的集合--文件名或文件夹名
     * @throws SftpException
     */
    public static void downloadFile(ChannelSftp sftp, String remotePath, String localPath, List<String> exceptList) throws SftpException {
        //获取当前远程目录的所有文件  --  该目录必须存在,否则会抛异常
        Vector vector = sftp.ls(remotePath);
        logger.debug("下载文件路径{},文件/文件夹总数{}", remotePath, vector.size());
        for (Object object : vector) {
            if (object instanceof ChannelSftp.LsEntry) {
                ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry) object;
                String filename = lsEntry.getFilename();
                SftpATTRS attrs = lsEntry.getAttrs();
                //只操作不在exceptList列表中的数据
                if (CollectionUtils.isEmpty(exceptList) || !exceptList.contains(filename)) {
                    if (attrs.isDir()) {
                        if (filename.length() == 0) {
                            continue;
                        }
                        String sub = filename.substring(filename.lastIndexOf(".") + 1, filename.length());
                        if (StringUtils.isBlank(sub)) {
                            logger.info("异常数据" + filename);
                            continue;
                        }
                        //如果当前操作对象为文件夹,则直接在本地创建相应的目录,并递归获取该目录下的文件
                        File file = new File(localPath + "/" + filename);
                        file.mkdir();
                        downloadFile(sftp, remotePath + "/" + filename, localPath + "/" + filename, exceptList);
                    } else {
                        //如果当前操作对象为文件夹,切换到当前服务器目录,并下载该文件到本地
                        sftp.cd(remotePath);
                        logger.debug("下载文件{}到{}", filename, localPath);
                        sftp.get(filename, localPath);
                    }
                }
            }
        }
    }

3、文件上传  -- 这里实现上传目录而非单个文件

jsch提供了一系列的put方法用于文件上传

/**
     * 上传整个目录文件到sftp服务器,会创建当前localPath根目录
     *
     * @param sftp       sftp连接
     * @param remotePath 当前要上传的远程目标目录
     * @param localPath  当前上传的本地文件目录
     * @throws SftpException
     */
    public static void uploadFile(ChannelSftp sftp, String remotePath, String localPath) throws SftpException {
        //获取当前操作的文件对象
        File rootFile = new File(localPath);
        String rootName = rootFile.getName();

        //切换到服务器当前目录  -- 该目录必须存在,否则会抛异常
        sftp.cd(remotePath);
        if (rootFile.isFile()) {
            //当前操作对象是文件,则上传文件到服务器
            sftp.put(rootName);
        } else {
            try {
                //切换到当前文件夹目录,不存在则异常
                sftp.cd(rootName);
            } catch (SftpException e) {
                //不存在,进入异常,创建目录
                sftp.mkdir(rootName);
            }
            //获取当前目录下的所有文件
            File[] files = rootFile.listFiles();
            for (File file : files) {
                if (file.isFile()) {
                    //当前操作对象是文件,则上传文件到服务器
                    sftp.put(localPath + "/" + file.getName(), remotePath + "/" + rootName);
                } else {
                    //如果当前操作对象是文件夹,则递归上传目录下的文件
                    uploadFile(sftp, remotePath + "/" + rootName, localPath + "/" + file.getName());
                }
            }
        }
    }

4、移动文件--这里实现整个目录的移动,根目录文件夹不删除。

jsch本身没有移动文件的方法,但提供了rename方法,可以将目标文件rename重命名到新的目录,以实现文件的移动--单个文件操作

/**
     * 重命名/移动文件到指定目录
     *
     * @param sftp     sftp连接
     * @param rootPath 当前操作目录 -- 作为透传根目录 不可删除
     * @param srcPath  文件夹源目录--绝对路径  源目录必须存在
     * @param dstPath  文件夹移动目标目录--绝对路径 最多允许一层不存在的目录
     * @throws SftpException
     */
    public static void renameFile(ChannelSftp sftp, String rootPath, String srcPath, String dstPath) throws SftpException {
        Vector vector = sftp.ls(srcPath);
        if (CollectionUtils.isEmpty(vector) && !rootPath.equals(srcPath)) {
            logger.info("源文件夹里没有文件");
            //删除空文件夹
            sftp.rmdir(srcPath);
            return;
        }

        try {
            //判断目标目录是否存在
            sftp.ls(dstPath);
        } catch (SftpException e) {
            //创建目标目录
            sftp.mkdir(dstPath);
        }

        for (Object object : vector) {
            if (object instanceof ChannelSftp.LsEntry) {
                ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry) object;
                String filename = lsEntry.getFilename();
                SftpATTRS attrs = lsEntry.getAttrs();
                if (attrs.isDir()) {
                    if (filename.length() == 0) {
                        continue;
                    }
                    String sub = filename.substring(filename.lastIndexOf(".") + 1, filename.length());
                    if (StringUtils.isBlank(sub)) {
                        logger.info("异常数据" + filename);
                        continue;
                    }
                    //正常文件夹是否存在
                    renameFile(sftp, rootPath, srcPath + SEPARATOR + filename, dstPath + SEPARATOR + filename);
                } else {
                    //如果是文件,则直接移动
                    sftp.rename(srcPath + SEPARATOR + filename, dstPath + SEPARATOR + filename);
                }
            }
        }
        //移动完成后,删除空目录
        if (CollectionUtils.isEmpty(sftp.ls(srcPath)) && !rootPath.equals(srcPath)) {
            sftp.rmdir(srcPath);
        }

    }

5、关闭sftp通道

/**
     * 断开sftp相关连接
     *
     * @param sftp
     * @throws JSchException
     */
    public static void closeSftp(ChannelSftp sftp) throws JSchException {
        if (sftp != null) {
            if (sftp.isConnected()) {
                sftp.disconnect();
            }
            Session session = sftp.getSession();
            if (session != null) {
                if (session.isConnected()) {
                    session.disconnect();
                }
            }
        }

    }

完整源码

import com.j1cn.commons.utils.StringUtils;
import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.util.List;
import java.util.Vector;

/**
 * sftp服务器操作工具类
 *
 * @author flysand
 **/
public class SftpUtils {

    private static final Logger logger = LoggerFactory.getLogger(SftpUtils.class);

    public static final String SEPARATOR = "/";

    public static final String RIGHT_SEPARATOR = "\";

    /**
     * 密码方式登录sftp服务器并获取连接
     *
     * @param host     sftp服务器地址
     * @param port     端口,默认22
     * @param user     sftp服务器登录用户
     * @param password sftp登录用户密码
     * @return
     * @throws JSchException
     */
    public static ChannelSftp getChannelSftp(String host, int port, String user, String password) throws JSchException {
        logger.debug("密码方式登录sftp服务器{},端口{}", host, port);
        JSch jSch = new JSch();
        Session session = jSch.getSession(user, host, port);
        session.setPassword(password);
        //设置首次登录是否需要检查
        session.setConfig("StrictHostKeyChecking", "no");
        //设置连接超时时间 -- 不设置会报session is down的错,暂未查到原因
        session.connect(30000);
        //获取sftp服务器连接
        Channel channel = session.openChannel("sftp");
        channel.connect();
        //返回sftp连接
        return (ChannelSftp) channel;
    }

    /**
     * 断开sftp相关连接
     *
     * @param sftp
     * @throws JSchException
     */
    public static void closeSftp(ChannelSftp sftp) throws JSchException {
        if (sftp != null) {
            if (sftp.isConnected()) {
                sftp.disconnect();
            }
            Session session = sftp.getSession();
            if (session != null) {
                if (session.isConnected()) {
                    session.disconnect();
                }
            }
        }

    }


    /**
     * 从sftp服务器下载当前目录下的所有文件和目录,不在本地创建当前remotePath根目录
     *
     * @param sftp       sftp服务器连接
     * @param remotePath 当前要下载的sftp远程服务器目录
     * @param localPath  当前要下载的文件本地目录
     * @param exceptList 不需要下载的集合--文件名或文件夹名
     * @throws SftpException
     */
    public static void downloadFile(ChannelSftp sftp, String remotePath, String localPath, List<String> exceptList) throws SftpException {
        //获取当前远程目录的所有文件  --  该目录必须存在,否则会抛异常
        Vector vector = sftp.ls(remotePath);
        logger.debug("下载文件路径{},文件/文件夹总数{}", remotePath, vector.size());
        for (Object object : vector) {
            if (object instanceof ChannelSftp.LsEntry) {
                ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry) object;
                String filename = lsEntry.getFilename();
                SftpATTRS attrs = lsEntry.getAttrs();
                //只操作不在exceptList列表中的数据
                if (CollectionUtils.isEmpty(exceptList) || !exceptList.contains(filename)) {
                    if (attrs.isDir()) {
                        if (filename.length() == 0) {
                            continue;
                        }
                        String sub = filename.substring(filename.lastIndexOf(".") + 1, filename.length());
                        if (StringUtils.isBlank(sub)) {
                            logger.info("异常数据" + filename);
                            continue;
                        }
                        //如果当前操作对象为文件夹,则直接在本地创建相应的目录,并递归获取该目录下的文件
                        File file = new File(localPath + "/" + filename);
                        file.mkdir();
                        downloadFile(sftp, remotePath + "/" + filename, localPath + "/" + filename, exceptList);
                    } else {
                        //如果当前操作对象为文件夹,切换到当前服务器目录,并下载该文件到本地
                        sftp.cd(remotePath);
                        logger.debug("下载文件{}到{}", filename, localPath);
                        sftp.get(filename, localPath);
                    }
                }
            }
        }
    }

    /**
     * 上传整个目录文件到sftp服务器,会创建当前localPath根目录
     *
     * @param sftp       sftp连接
     * @param remotePath 当前要上传的远程目标目录
     * @param localPath  当前上传的本地文件目录
     * @throws SftpException
     */
    public static void uploadFile(ChannelSftp sftp, String remotePath, String localPath) throws SftpException {
        //获取当前操作的文件对象
        File rootFile = new File(localPath);
        String rootName = rootFile.getName();

        //切换到服务器当前目录  -- 该目录必须存在,否则会抛异常
        sftp.cd(remotePath);
        if (rootFile.isFile()) {
            //当前操作对象是文件,则上传文件到服务器
            sftp.put(rootName);
        } else {
            try {
                //切换到当前文件夹目录,不存在则异常
                sftp.cd(rootName);
            } catch (SftpException e) {
                //不存在,进入异常,创建目录
                sftp.mkdir(rootName);
            }
            //获取当前目录下的所有文件
            File[] files = rootFile.listFiles();
            for (File file : files) {
                if (file.isFile()) {
                    //当前操作对象是文件,则上传文件到服务器
                    sftp.put(localPath + "/" + file.getName(), remotePath + "/" + rootName);
                } else {
                    //如果当前操作对象是文件夹,则递归上传目录下的文件
                    uploadFile(sftp, remotePath + "/" + rootName, localPath + "/" + file.getName());
                }
            }
        }
    }

    /**
     * 重命名/移动文件到指定目录
     *
     * @param sftp     sftp连接
     * @param rootPath 当前操作目录 -- 作为透传根目录 不可删除
     * @param srcPath  文件夹源目录--绝对路径  源目录必须存在
     * @param dstPath  文件夹移动目标目录--绝对路径 最多允许一层不存在的目录
     * @throws SftpException
     */
    public static void renameFile(ChannelSftp sftp, String rootPath, String srcPath, String dstPath) throws SftpException {
        Vector vector = sftp.ls(srcPath);
        if (CollectionUtils.isEmpty(vector) && !rootPath.equals(srcPath)) {
            logger.info("源文件夹里没有文件");
            //删除空文件夹
            sftp.rmdir(srcPath);
            return;
        }

        try {
            //判断目标目录是否存在
            sftp.ls(dstPath);
        } catch (SftpException e) {
            //创建目标目录
            sftp.mkdir(dstPath);
        }

        for (Object object : vector) {
            if (object instanceof ChannelSftp.LsEntry) {
                ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry) object;
                String filename = lsEntry.getFilename();
                SftpATTRS attrs = lsEntry.getAttrs();
                if (attrs.isDir()) {
                    if (filename.length() == 0) {
                        continue;
                    }
                    String sub = filename.substring(filename.lastIndexOf(".") + 1, filename.length());
                    if (StringUtils.isBlank(sub)) {
                        logger.info("异常数据" + filename);
                        continue;
                    }
                    //正常文件夹是否存在
                    renameFile(sftp, rootPath, srcPath + SEPARATOR + filename, dstPath + SEPARATOR + filename);
                } else {
                    //如果是文件,则直接移动
                    sftp.rename(srcPath + SEPARATOR + filename, dstPath + SEPARATOR + filename);
                }
            }
        }
        //移动完成后,删除空目录
        if (CollectionUtils.isEmpty(sftp.ls(srcPath)) && !rootPath.equals(srcPath)) {
            sftp.rmdir(srcPath);
        }

    }
}
原文地址:https://www.cnblogs.com/flysand/p/7985725.html