首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >jsch实现与服务器完成文件相关操作

jsch实现与服务器完成文件相关操作

作者头像
sucl
发布2019-08-07 15:15:58
1.7K0
发布2019-08-07 15:15:58
举报
文章被收录于专栏:企业平台构建企业平台构建

以前为了实现文件上传服务器的功能,于是在晚上搜了下,发现可以通过jsch来实现,同时发现jsch还是与服务器间通过一些命令完成其他操作,觉得不可思议,但是当时也没有过多的了解。

而这次需要完成从从服务器下拉文件,开始想到用ftp完成,但是发现借助客户端不是太好实现,或者确实不太了解这方面的知识,想到以前用过jsch,既然能够完成文件的上传,那么是否同样能够完成文件的下载呢?

当然在使用前还是会先查阅一番,如果确实可以实现,当然就会深入去了解,看了一些博客,在https://www.cnblogs.com/weiyi1314/p/9517245.html中写道可以通过 put实现文件上传;get实现文件下载。

建立连接:

public void connect(){
        try {
            JSch jsch = new JSch();
            jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
            sshSession = jsch.getSession(config.getUsername(), config.getHost(), config.getPort());
            sshSession.setConfig("PreferredAuthentications","password");//关闭gssapi认证
            sshSession.setConfig("StrictHostKeyChecking","no");// 设置第一次登陆的时候提示
            sshSession.setPassword(config.getPassword());

            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
//            log.info("Session connected!");
            channel = sshSession.openChannel("sftp");
            channel.connect();
//            log.info("Channel connected!");
            this.sftp = (ChannelSftp) channel;
            this.sftp.setFilenameEncoding("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

文件下载:

public void fileDownload(String path,String dist,boolean force){
    try {
        String fileNmae = StringUtils.getFilename(path);

        InputStream is = sftp.get(path);
        FileUtils.copyInputStreamToFile(is, FileUtils.getFile(dist,fileNmae,force));
        is.close();
    } catch (SftpException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

测试:

public static void main(String[] args) throws SftpException {
        SftpConnection connection = new SftpConnection(new SftpConfig());
        connection.connect();
        ChannelSftp sftp = connection.getSftp();

        // /data/test/1.tar.gz
//        connection.fileDownload("/data/test/1.txt","D:\\test",false);
        connection.close();
    }

确实可以实现,单个文件的下载也就没什么问题了,当然往往实际业务不会这么简单,如果是从目录中遍历下载文件会如何?下载文件性能怎么样?是否还有一些其他的方法可以完成更多的功能?下面来从几个方面了解下。

  • 目录遍历

查看了相关的方法,发现又一个ls方法,同时有一个重载方法,使用都是一样,只不过一个没有返回值,一个需要我们自己处理返回值,这个方法可以将制定目录的内容返回,那么遍历无法就是取出目录中的目录递归遍历了:

public List<String> listFiles(String path){
    List<String> list = new ArrayList<>();
    ChannelSftp.LsEntrySelector selector = new ChannelSftp.LsEntrySelector() {
        @Override
        public int select(ChannelSftp.LsEntry entry) {
            String filename = entry.getFilename();
            if(entry.getAttrs().isReg()){
                list.add("f:"+filename);
            }else if(entry.getAttrs().isDir()){
                if(!".".equals(filename)&&!"..".equals(filename)){
                    list.add("d:"+filename);
                    list.addAll(listFiles(path+"/"+filename));
                }
            }
            return ChannelSftp.LsEntrySelector.CONTINUE;
        }
    };
    try {
        sftp.ls(path,selector);
    } catch (SftpException e) {
        e.printStackTrace();
    }
    return list;
}

想想是没问题,但是真要这么处理还真的不行,首先要明白一点,遍历的目录中,会默认的加上"."、".."这两个,所有在操作的时候把这个排除了。

但是还是出现了异常:

先说明下,我的目录结构:

-- test
  -- 1.txt
  -- a
    -- 2.txt

第一层目录的文件没了?

经过断点发现以下奇怪的地方:

在方法中可以看到,在遇到文件直接保存,遇到目录则递归处理,而这个值出现的则是在递归跳出后目录中的对象,而且根本就不存在这个文件夹。

看了下ls的方法:

发现我们在对某个目录进行操作是,是修改了ChannelSftp的成员变量,而在下次处理其他目录的时候,这些属性确实改变后的值,不知道这个是不是导致出现这种问题的原因?

安装这种思路,我们让每次对目录的处理完成后再处理新的目录,方法改写如下:

public List<String> listFiles(String path,boolean justFile,boolean isFullPath){
    List<String> list = new ArrayList<>();
    List<String> cachePaths = new ArrayList<>();
    Vector vector = null;
    try {
        vector = sftp.ls(path);
    } catch (SftpException e) {
        e.printStackTrace();
    }
    if(vector!=null){
        Iterator it = vector.iterator();
        while (it.hasNext()){
            ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) it.next();
            String filename = entry.getFilename(),fullPath = path+"/"+filename;
            if(entry.getAttrs().isDir()){
                if(!".".equals(filename) && !"..".equals(filename)){
                    if(!justFile){
                        list.add(isFullPath?fullPath: filename);
                    }
                    cachePaths.add(fullPath);
                }
            }else if(entry.getAttrs().isReg()){
                list.add(isFullPath?fullPath: filename);
            }

        }
    }
    if(cachePaths.size()>0){
        for(String cachePath : cachePaths){
            list.addAll(listFiles(cachePath,justFile,isFullPath));
        }
    }
    return list;
}

经过测试也确实没有出现问题了。

  • 遍历下载

在目录遍历完成后,遍历下载相对比较简单了,使用上面的方法,先将文件去不取出,然后遍历下载:

public void directoryDownload(String path,String dist,boolean force){
    List<String> files = listFiles(path, true, true);
    if(files.size()>0){
        for(String file : files){
            String offsetPath = file.substring(path.length()+1,file.length()-StringUtils.getFilename(file).length());
            fileDownload(file,dist+File.separator+offsetPath,force);
        }
    }
}
  • 性能分析

其实真正做测试时,返现系统启动会非常慢,在网上查了下,

断点可以发现主要在方法

Session.connect()

时比较慢,当然我们可以做处理,比如系统启动时就先连接,如果负载过大,我们还可以引入连接池的概念。

上面在文件下载时,说到了遍历下载,如果文件较多或较大时,如何提高下载效率?同时如果下载过程出现问题,如何保证可靠性?

  • 其他操作

按需补充

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档