Java使用JSch实现SSH远程执行命令

前言

有一个奇怪的需求,就是将本地的内容直接提交到远程服务器,并且需要针对文件夹进行处理。所以,这里就直接采用JSch来实现。在这里,感谢秀发浓密的程序猿这篇博客,给了很大启发。

思路

既然是直接读取文件夹,我们就不考虑浏览器不能访问本地文件的问题了。直接就是Java处理,然后直接读取文件提交服务器。

当然,介于上传过程中是Windows上传到Linux,如果只传图片倒还没什么问题,传文件可就造了老罪了。还好我现在只有图片需求。

但也并不能掉以轻心,因为,就算是图片,名字也是中文的。

所以直接用UUID避免了上传乱码,等上传结束了再mv到目标文件夹中。

上代码

java 复制代码
private static final String TMPDIR = "/data/ftp";
public static void sshSftpUpload(String ip, String user, String psw, int port, String localDirFileName, String destDir, String fileName) throws Exception {
  // 这些需要后期关闭的资源需要单独拉出来
  // 而由于OutputStream与InputStream在初始化时需要单独Override其中的write方法
  // 所以没办法直接用`try-catch-resource`语法糖给直接new出来
  // 所以单独声明,就干脆全放一起了
  Channel channel = null, channelShell = null;
  Session session = null;
  ChannelSftp sftp = null;
  String tmpFileName = "";
  OutputStream outStream = null;
  InputStream inStream = null;
  try {
    JSch jsch = new JSch();
    // 如果端口非法,使用默认端口
    if (port <= 0)
      session = jsch.getSession(user, ip);
    else
      session = jsch.getSession(user, ip, port);
    // 如果服务器连接不上,则抛出异常
    // 在真正返回值的地方再处理异常,单凭返回值没办法细致处理问题
    if (session == null)
      throw new Exception("session is null");
    // 设置主机令牌
    session.setPassword(psw);
    //设置第一次登陆的时候提示,可选值:(ask | yes | no)
    session.setConfig("StrictHostKeyChecking", "no");
    //设置登陆超时时间(30s)
    session.connect(30 * 1000);

    //创建sftp通信通道
    channel = session.openChannel("sftp");
    channel.connect(1000);

    // 强制转换,否则Channel类无法使用cd方法
    sftp = (ChannelSftp) channel;
    // 进入临时目录文件夹
    sftp.cd(TMPDIR);

    // 以下代码实现从本地上传一个文件到服务器
    // 上传时中文可能乱码,采用UUID规避
    tmpFileName = UUID.randomUUID().toString().replace("-", "").toLowerCase() + "." + fileName.substring(fileName.lastIndexOf('.') + 1);
    outStream = sftp.put(tmpFileName);
    // Files的构造函数是私有的,经典的单例模式,在项目中表现更佳
    inStream = Files.newInputStream(new File(localDirFileName).toPath());
    // 一如既往的上传操作
    byte[] b = new byte[1024];
    int n;
    while ((n = inStream.read(b)) != -1)
      outStream.write(b, 0, n);
    // 需要注意的是,你完全可以在这里调用flush,就像这样
    // outStream.flush();
    // 但flush往往会造成系统级IO操作
    // 所以个人不愿意在这里flush,而是在finally中flush
    
    // 传输完成  将文件转移到正式目录 - 注:exec不起作用
    channelShell = session.openChannel("shell");
    // 需要放置回车符模拟Shell的回车操作
    channelShell.setInputStream(new ByteArrayInputStream(
      ("cp " + TMPDIR + "/" + tmpFileName + " " + destDir + "/" + fileName + " \n")
      .getBytes(StandardCharsets.UTF_8)));
    // 将输出重定向到控制台
    channelShell.setOutputStream(System.out);
    channelShell.connect(5 * 1000);

  } catch (Exception e) {
    throw new Exception(e.getMessage());
  } finally {
    // 结束后关闭资源,有的是close,有的是flush,有的是disconnect
    if (outStream != null) {
      outStream.flush();
      outStream.close();
    }
    if (inStream != null) inStream.close();
    if (sftp != null) {
      // 删除临时文件
      sftp.rm(tmpFileName);
      sftp.disconnect();
    }
    if (session != null) session.disconnect();
    if (channel != null) channel.disconnect();
    if (channelShell != null) channelShell.disconnect();
  }
}

虽然finally很长,但是起码交付没啥问题。

主打一个面向测试用例编程了。

相关推荐
逝水如流年轻往返染尘几秒前
JAVA中的String类
java
S1998_1997111609•X2 分钟前
论next/js在打击省份及犯罪行为集团的系统分析[特殊字符]设计
网络·数据库·百度·ssh·开闭原则
一只叫煤球的猫3 分钟前
ThreadForge 1.2.0 发布:让 Java 并发代码更好写,这次补齐了高阶编排、示例与观测能力
java·设计模式·设计
counting money9 分钟前
Spring框架基础(依赖注入-半注解形式)
java·后端·spring
CN-Dust9 分钟前
【C++】for循环例题专题
java·c++·算法
染夕陌木22 分钟前
RPC/服务调用框架中“方法无法应用到给定类型”错误的通用排查指南
java·ide·rpc
大大杰哥26 分钟前
String常用方法
java
AI人工智能+电脑小能手42 分钟前
【大白话说Java面试题】【Java基础篇】第20题:HashMap在计算index的时候,为什么要对数组长度做减1操作
java·开发语言·数据结构·后端·面试·哈希算法·hash-index
嵌入式×边缘AI:打怪升级日志44 分钟前
嵌入式Linux开发(了解交叉编译工具链的组成)
java·linux·运维
FreeGo~1 小时前
Linux 系统编程 进程篇 (五)
java·linux·服务器