我是靠谱客的博主 包容绿草,最近开发中收集的这篇文章主要介绍java sftp工具类(支持多线程下载),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

最近需要从sftp上下载文件。我看到网上大部分都不支持多线程下载很多都会卡死,或者是排队下载。我也从网上copy了一份代码,看了一下。发现了原因。

  1. 卡死原因
    因为很多都是一个session对应一个channel,但是没有做并发处理,导致有的session未被关闭。会话一直存在,它可以被垃圾回收,但是要等到下次gc时才会关闭该session。
  2. 排队下载
    这就是对上面进行了并发处理,让其同步创建session,然后session创建channel,并且同步关闭channel和session。这样也就下载完成了。但是他们是同步下载,所以需要排队。
  3. 一个session个channel
    如果是一个session多个channel,那么当所有channel下载完成关闭时,如果当前session没被关闭,那么所有的数据将停留在内存中,有时候你会发现,你结束掉java进程的时候,它所有数据都被flush到文件了。也就是关闭session。

我拿着改进了一下,将其每个下载对应一个session和一个channel。那么意味着每个session->channel是独立存在的,不存在冲突问题。下面是代码,可以参考下:

import com.jcraft.jsch.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.io.*;
import java.util.Properties;

/**
 * @author xoxo
 * @description sftp支持多线程下载
 */
@Slf4j
@Data
public class SftpClient {
    private static String host = "192.168.10.130";

    private static String username = "foo";

    private static String password = "pass";

    protected static String privateKey;// 密钥文件路径

    protected static String passphrase;// 密钥口令

    private static int port = 2222;

    @Data
    @AllArgsConstructor
    private static class SftpObject{
        private Session session;
        private ChannelSftp channelSftp;
    }

    public static SftpObject connect() {
        JSch jsch = new JSch();
        ChannelSftp sftp = null;
        Channel channel = null;
        Session session = null;
        try {
            if (!StringUtils.isEmpty(privateKey)) {
                // 使用密钥验证方式,密钥可以使有口令的密钥,也可以是没有口令的密钥
                if (!StringUtils.isEmpty(passphrase)) {
                    jsch.addIdentity(privateKey, passphrase);
                } else {
                    jsch.addIdentity(privateKey);
                }
            }
            session = jsch.getSession(username, host, port);
            if (!StringUtils.isEmpty(password)) {
                session.setPassword(password);
            }
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");// do not verify host
            // key
            session.setConfig(sshConfig);
            // session.setTimeout(timeout);
            // session.setServerAliveInterval(92000);
            session.connect();
            // 参数sftp指明要打开的连接是sftp连接
            channel = session.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
        } catch (JSchException e) {
            log.error("连接【" + host + ":" + port + "】异常", e);
        }
        return new SftpObject(session,sftp);
    }


    /**
     * 关闭资源
     */
    public static void disconnect(SftpObject sftpObject) {
        if(sftpObject==null){
            return;
        }
        ChannelSftp channelSftp = sftpObject.getChannelSftp();
        Session session = sftpObject.getSession();
        if (channelSftp != null) {
            if (channelSftp.isConnected()) {
                channelSftp.disconnect();
            }
        }
        if (session != null) {
            if (session.isConnected()) {
                session.disconnect();
            }
        }
    }

    /**
     * 下载单个文件
     *
     * @param remoteFileName
     *            下载文件名
     * @param localPath
     *            本地保存目录(以路径符号结束)
     * @param localFileName
     *            保存文件名
     * @return
     */
    public static  boolean downloadFile(String remotePath, String remoteFileName, String localPath, String localFileName) {
        log.info(remotePath + "/" + remoteFileName + "/" + localPath + "/" + localFileName);
        SftpObject sftpObject = null;
        try {
            sftpObject = connect();
            ChannelSftp channelSftp = sftpObject.getChannelSftp();
            channelSftp.cd(remotePath);
            File file = new File(localPath + localFileName);
            mkdirs(localPath + localFileName);
            channelSftp.get(remoteFileName, new FileOutputStream(file));
            return true;
        } catch (FileNotFoundException e) {
            log.error("不存在文件,Path:" + remotePath + ",file:" + remoteFileName, e);
        } catch (SftpException e) {
            log.error("下载文件处理异常,Path:" + remotePath + ",file:" + remoteFileName, e);
        } finally {
            disconnect(sftpObject);
        }
        return false;
    }
    /**
     * 如果目录不存在就创建目录
     *
     * @param path
     */
    private static void mkdirs(String path) {
        File f = new File(path);
        String fs = f.getParent();
        f = new File(fs);
        if (!f.exists()) {
            f.mkdirs();
        }
    }

    public static void main(String[] args){
        new Thread(()->{
            downloadFile("/upload","java.pdf","D:/work/","xoxo1.pdf");
        }).start();
        new Thread(()->{
            downloadFile("/upload","java.pdf","D:/work/","xoxo2.pdf");
        }).start();
        new Thread(()->{
            downloadFile("/upload","java.pdf","D:/work/","xoxo3.pdf");
        }).start();
    }
}

最后

以上就是包容绿草为你收集整理的java sftp工具类(支持多线程下载)的全部内容,希望文章能够帮你解决java sftp工具类(支持多线程下载)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部