Java中java.io包为我们提供了输入流和输出流,对文件的读写基本上都依赖于这些封装好的关于流的类中来实现。前段时间遇到了以下两种需求:
1、与某系统对接,每天获取最新的图片并显示在前端页面。该系统提供的是一个http协议的图片URL,本来获取到该系统的图片地址以后在HTML中显示就可以了,但是该系统不太稳定,图片URL经常不能使用,而且每天生成图片不一定成功,
对自己系统的功能影响很大,emm。。。所以就考虑每天从该系统下载最新的图片到本地更新保存,没有新图片就继续使用上次的图片。
2、公司算法团队的同事完成了一个视频分析的检测功能,会生成一些截取的短视频文件,系统需要获取并保存这些视频文件。算法运行在Linux系统,没有搭建FTP服务器,所以就需要系统从运行算法的Linux系统复制文件到系统本地保存起来。
这两个需求实现起来都是大同小异,思路就是读取文件然后写到指定路径,只不过http协议的图片地址可以直接读取然后保存,而Linux系统的文件需要远程连接到该服务器然后再下载文件,这就需要我们引入以下依赖:
<!--远程读取服务器文件--> <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-ssh2</artifactId> <version>262</version> </dependency> <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.53</version> </dependency> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.4</version> </dependency>
废话不说了,先上代码:
package com.XXX.utils; import ch.ethz.ssh2.*; import com.databus.Log; import com.mysql.jdbc.StringUtils; import java.io.*; import java.net.*; public class FileUtils { /* * 根据http路径下载文件保存到指定路径 * urlString:文件路径 * fileName:保存到本地的文件名称 * filePath:本地要保存的指定路径 * */ public static boolean downloadFile(String urlString,String fileName,String filePath) { boolean bool = false; InputStream is = null; FileOutputStream os = null; try { Log.info("文件路径:" + urlString); // 构造URL java.net.URL url = new java.net.URL(urlString); // 打开连接 URLConnection con = url.openConnection(); // 输入流 is = con.getInputStream(); // 1K的数据缓冲 byte[] bs = new byte[1024]; // 读取到的数据长度 int len; //判断指定目录是否存在,不存在则先创建目录 File file = new File(filePath); if (!file.exists()) file.mkdirs(); //fileName如果不包含文件后缀,则需要加上后缀,如:fileName + ".jpg";fileName + ".txt"; os = new FileOutputStream(filePath + fileName, false);//false:覆盖文件,true:在原有文件后追加 // 开始读取 while ((len = is.read(bs)) != -1) { os.write(bs, 0, len); } bool = true; Log.info("文件保存成功:" + fileName); }catch (Exception e){ e.printStackTrace(); }finally { // 完毕,关闭所有链接 if (null != os){ try { os.flush(); os.close(); }catch (IOException e){ e.printStackTrace(); } } if (null != is){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return bool; } //远程下载服务器文件 public static boolean copyFile(String ip,int port,String userName,String password,String sourceFile,String targetFile,String targetFileName){ boolean bool = false; Connection conn = null; Session session = null; try { if (StringUtils.isNullOrEmpty(ip) || StringUtils.isNullOrEmpty(userName) || StringUtils.isNullOrEmpty(password) || StringUtils.isNullOrEmpty(sourceFile) || StringUtils.isNullOrEmpty(targetFile)){ return bool; } conn = new Connection(ip,port); conn.connect(); boolean isAuth = conn.authenticateWithPassword(userName,password); if (!isAuth){ Log.info("算法主机连接失败"); return bool; } //执行命令 session = conn.openSession(); //执行命令并打印执行结果 session.execCommand("df -h"); InputStream staout = new StreamGobbler(session.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(staout)); String line = null; while ((line = br.readLine()) != null){ System.out.println(line); } br.close(); //下载文件到本地 SCPClient scpClient = conn.createSCPClient(); SCPInputStream scpis = scpClient.get(sourceFile); //判断指定目录是否存在,不存在则先创建目录 File file = new File(targetFile); if (!file.exists()) file.mkdirs(); FileOutputStream fos = new FileOutputStream(targetFile + targetFileName); byte[] buffer = new byte[1024]; int len = 0; while ((len = scpis.read(buffer)) != -1){ fos.write(buffer,0,len); } fos.close(); bool = true; //SFTP /*SFTPv3Client sftPClient = new SFTPv3Client(conn); sftPClient.createFile(""); sftPClient.close();*/ }catch (Exception e){ e.printStackTrace(); Log.info(e.getMessage()); Log.info("保存失败:" + sourceFile); }finally { if (null != session){ session.close(); } if (null != conn) { conn.close(); } } return bool; } }
第一个方法downloadFile(String urlString,String fileName,String filePath)与我们读写文件没什么区别,我们主要说一下远程读取Linux服务器文件的方法:
copyFile(String ip,int port,String userName,String password,String sourceFile,String targetFile,String targetFileName)
可以看到,我们需要Linux服务器的ip、ssh开放的端口号(一般默认是22)、服务器用户名和密码,所以我们要确保Linux服务器已经开放ssh连接,否则我们的程序根本就连不上服务器,更不要说复制文件了。
其实ssh2连接远程服务器,就和我们用Xshell连接服务器是一样的,不但可以复制文件,也可以执行Linux命令对Linux进行操作,看上面的一段代码:
//执行命令 session = conn.openSession(); //执行命令并打印执行结果 session.execCommand("df -h"); InputStream staout = new StreamGobbler(session.getStdout()); BufferedReader br = new BufferedReader(new InputStreamReader(staout)); String line = null; while ((line = br.readLine()) != null){ System.out.println(line); } br.close();
这段代码与复制文件没有关系,之所以保留就是要说明一下,我们执行了df -h 命令(查询服务器磁盘使用情况),将会得到磁盘的具体使用情况,与下图效果相同。所以我们可以用ssh2做很多事情,有兴趣的童鞋可以多了解。
另外在copyFile()方法的最后有一段注释的代码:
//SFTP /*SFTPv3Client sftPClient = new SFTPv3Client(conn); sftPClient.createFile("files"); sftPClient.close();*/
我们也可以通过建立SFTP连接来远程操作目录和文件,比如:创建、删除目录,创建、删除文件等。
关于ssh2包的其他功能和用法不再引申,有兴趣的童鞋欢迎在评论区交流。