最近公司有个数据对接需求,合作方那边是使用我们这边的系统进行出单的,数据首先也是在我们这边。后面他们自己开发了业务系统,需要我们这边定时把每天的数据传送到那边去。他们那边开发部门要求我们这边,按一定的格式导出加签加密的数据文件到他们的sftp服务器上面去。sftp代码之前同事有写过类似的代码,就把他的给我啦,既然有了代码,当然要消化成自己的知识啦!这个过程中也遇到一些坑,就是我们上传到sftp目录的时候,文件一多我们就得隔开文件夹,一般都是按年月隔开的,但是sftp切换目录后,如果目录不存在,我们就新建目录,这样的逻辑是没毛病,但是它没办法一次建多级目录,比如/home 下面可以建/home/2022/ 没办法建/home/2022/03(会报异常),所以这个的话我们还得用循环来做。
一、代码
先给大家看下代码:
String userName = "";//账号 String password = ""; //密码 String host = ""; //ip int port=8889; //端口号 String sftpUrl = "/application/data"; //上传到sftp服务器 路径 String publicUrl = "E://"; //要上传的文件 String yyyy = DateUtils.formatDate(new Date(), "yyyy"); String mm = DateUtils.formatDate(new Date(), "MM"); String uploadUrl = sftpUrl + "/" + yyyy + "/" + mm; //存储目录地址 List dirs = new ArrayList(); dirs.add(0, sftpUrl + "/" + yyyy + "/"); dirs.add(1, sftpUrl + "/" + yyyy + "/" + mm + "/"); try { //1.获取会话 JSch jSch = new JSch(); Session session = jSch.getSession(userName, host, port); session.setPassword(password); Properties properties = new Properties(); properties.put("StrictHostKeyChecking", "no"); session.setConfig(properties); session.connect(); //2.获取上传的管道 Channel channel = session.openChannel("sftp"); channel.connect(); sftp = (ChannelSftp) channel; if (!(uploadUrl == null || uploadUrl.trim().length() == 0)) { for (int index = 0; index < dirs.size(); index++) { //目录不存在,新建目录 try { sftp.cd(uploadUrl);//进入指定目录 break; } catch (SftpException e1) { try { sftp.cd(dirs.get(index).toString()); } catch (SftpException e2) { try { sftp.mkdir(dirs.get(index).toString()); sftp.cd(dirs.get(index).toString()); } catch (SftpException e3) { logger.error("SFTP切换目录出错!", e3); } } } } } //读取源文件流(客户端文件) InputStream client_fileInput = new FileInputStream(publicUrl ); sftp.put(client_fileInput, “上传后的文件名”); //传送到服务端 //5.关闭 sftp.disconnect(); client_fileInput.close(); channel.disconnect(); session.disconnect(); logger.info("sftp传输成功"); } catch (Exception e) { logger.info("sftp传输失败: " + e.toString()); e.printStackTrace(); }
一般上传完文件后,需要删除本地的文件以免占用空间
/** * 迭代删除文件夹 * @param dirPath 文件夹路径 */ public static void deleteDir(String dirPath) { File file = new File(dirPath); if(!file.exists()){ return; } if(file.isFile()) { file.delete(); }else { File[] files = file.listFiles(); if(files == null) { file.delete(); }else { for (int i = 0; i < files.length; i++) { deleteDir(files[i].getAbsolutePath()); } file.delete(); } } }
这里我遇到个坑,就是你们发现文件夹或者文件没删除或者没删除干净,就要检查一下流有没有关闭,传递流的时候最好不要直接 xxx方法(new FileInputStream),用下面这种方式比较好,好排查问题,哈哈。
FileInputStream file=new FileInputStream(“”); xxx方法(file);file.close();
二、sftp连接过程
2.1、 JSch简介
JSch库可以实现Java连接Linux服务器并操作命令、文件等,支持常用的各种授权模式。
3.1、JSch建立会话连接
从JSch对象中获取Session,用于连接,并设置连接信息,账号、密码、服务器地址、端口号。
3.2 、 设置连接的参数配置
StrictHostKeyChecking=no 最不安全的级别,当然也没有那么多烦人的提示了,相对安全的内网测试时建议使用。如果连接server的key在本地不存在,那么就自动添加到文件中(默认是known_hosts),并且给出一个警告。 StrictHostKeyChecking=ask 默认的级别。如果连接和key不匹配,给出提示,并拒绝登录。 StrictHostKeyChecking=yes 最安全的级别,如果连接与key不匹配,就拒绝连接,不会提示详细信息。
3.3、获取文件上传的通道
通过以上的步骤,我们与sftp服务器建立了连接后,我们需要获取文件的传输通道Channel,并指定为sftp服务,随便进行connect()连接。
3.3、常用命令
获取了sftp通道,我们可以类似在linux下简单做一些shell指令操作:
mkdir()方法 创建目录,只允许创建一级目录
cd()方法 切换目录
put()方法 上传文件
rm()方法 删除文件
还有很多,大家可以直接看对象的对应方法就知道了,方法命名跟我们平时用的linux命令差不多。