#博学谷IT学习技术支持#
目录
主动模式和被动模式
主动模式和被动模式区别简概述
1. ① 主动模式数据传送是 "服务器" 连接到 "客户端" 端口;
② 被动模式数据传送是 "客户端" 连接到 "服务器" 端口。
2. ① 主动模式需要客户端必须开放端口给服务器,大部分客户端都是在防火墙内,开放端口给FTP服务器访问比较困难;
② 被动模式只需要服务器端开放端口给客户端连接。
注意点: 被动模式和主动模式的登录过程,都是FTP客户端去连接FTP服务器。
通过上述: 如何选择FTP的连接模式,需要自身业务环境及客户需求决定
FTPUtils案例
public class FTPUtils { private static int timeout = 60000; //超时数 private static final String defaultFtpEncode = "UTF-8"; //字符集 private static String sftp = "sftp"; //sftp private static String ftp = "ftp"; //sftp private static String session = "session"; //session private static String ENTER_LOCAL_ACTIVE_MODE = "enterLocalActiveMode"; //主动模式 /** * 传入一个通道对象 * <p> * username 远程要连接的服务器的用户名 * password 远程要连接的服务器的密码 * ip 远程服务器ip * port 远程服务器的ssh服务端口 * ChannelSftp返回指向这个通道指定的地址的channel实例 * xException */ public static HashMap<String, Object> connectSFTP(FtpSendVO ftpSendVO, ProxyConfiguration proxyConfiguration) { ChannelSftp channelSftp; //基础参数 String password = ftpSendVO.getPassword();//密码 String account = ftpSendVO.getAccount();//登录账户 String ip = ftpSendVO.getHostName();//ip String port = ftpSendVO.getPort();//端口 String keyFilePath = ftpSendVO.getKeyFilePath();//sshKey临时文件路径 try { JSch jsch = new JSch(); // 判断秘钥是否有秘钥 if (keyFilePath != null) { // 判断是否有秘钥密码 if (password != null && !"".equals(password)) { jsch.addIdentity(keyFilePath, password); } else { jsch.addIdentity(keyFilePath, ""); } } Session sshSession = jsch.getSession(account, ip, Integer.parseInt(port)); if (log.isInfoEnabled()) { log.info("Session created."); } // 判断是否有密码 if (password != null) { sshSession.setPassword(password); } Properties sshConfig = new Properties(); //为Session对象设置properties sshSession.setConfig(sshConfig); sshSession.setTimeout(timeout); sshSession.connect(); //建立SFTP通道的连接 if (log.isInfoEnabled()) { log.info("Session connected."); } // 打开SFTP通道 Channel channel = sshSession.openChannel("sftp"); channel.connect(); if (log.isInfoEnabled()) { log.info("Opening Channel."); } channelSftp = (ChannelSftp) channel; if (log.isInfoEnabled()) { log.info("Connected to " + ip + "."); } HashMap<String, Object> loginInfo = new HashMap<>(); loginInfo.put(sftp, channelSftp); loginInfo.put(session, sshSession); return loginInfo; } catch (Exception e) { log.error("SFTP连接服务器失败:", e); throw new xException("SFTP连接服务器失败: " + e.getMessage(), e); } finally { File file = ftpSendVO.getFile(); if (file != null) { FileUtils.deleteQuietly(file); } } } public static HashMap<String, Object> connectFTP(FtpSendVO ftpSendVO, ProxyConfiguration proxyConfiguration) { FTPClient ftpClient; String password = ftpSendVO.getPassword(); String account = ftpSendVO.getAccount(); String ip = ftpSendVO.getHostName(); String port = ftpSendVO.getPort(); HashMap<String, Object> loginInfo = new HashMap<>(); ftpClient = new FTPClient(); try { ftpClient.connect(ip, Integer.parseInt(port)); ftpClient.login(account, password); ftpClient.setType(FTPClient.TYPE_BINARY); ftpClient.setCharset(ftpSendVO.getServerCharset()); System.setProperty("ftp4j.timeout", String.valueOf(timeout)); //记录返回信息 loginInfo.put(ftp, ftpClient); if (!ftpClient.isConnected()) { log.error("FTP服务器连接失败"); throw new xException("FTP连接服务器失败"); } return loginInfo; } catch (xException e) { log.error("FTP登录失败 message:" + e.getMessage(), e); throw e; } catch (Exception e) { log.error("FTP登录失败 message:" + e.getMessage(), e); throw new xException("FTP连接服务器失败 message:" + e.getMessage(), e); } } /*********************************************退出登录*********************************************************/ /** * 退出FTP登录 */ public static Boolean ftpLoginOut(HashMap<String, Object> hashMap) { FTPClient ftpClient = (FTPClient) hashMap.get(ftp); log.info("准备关闭FTP连接:" + ftpClient); boolean b = false; if (ftpClient != null && ftpClient.isConnected()) { try { ftpClient.disconnect(true); b = true; } catch (Exception e) { log.error("关闭FTP连接失败 message:" + e.getMessage(), e); // throw new xException("关闭FTP连接失败", e); } boolean disconnectSuccess = !ftpClient.isConnected(); if (disconnectSuccess) { log.info("关闭FTP连接成功"); } else { log.info("关闭FTP连接失败"); } } return b; } /** * sftp退出登陆 * <p> * sftp对象 * session session对象 */ public static Boolean sftpLoginOut(HashMap<String, Object> hashMap) { Boolean flag = false; ChannelSftp channelSftp = (ChannelSftp) hashMap.get(sftp); Session channelSession = (Session) hashMap.get(session); try { if (null != channelSftp && channelSftp.isConnected()) { channelSftp.disconnect(); } if (null != channelSession && channelSession.isConnected()) { channelSession.disconnect(); } flag = true; } catch (Exception e) { log.error("用户退出SFTP服务器出现异常:" + e.getMessage(), e); } return flag; } /***************************************上传文件**************************************************/ /** * Sftp上传文件 * * @param uploadPath 上传SFTP完整路径 * @param uploadFileList 上传文件路径] * @param loginInfo 认证方式(参数) */ public static void uploadSftpFile(String uploadPath, List<File> uploadFileList, HashMap<String, Object> loginInfo) { Assert.notNull(uploadPath, "uploadPath is not null"); Assert.notNull(uploadFileList, "uploadFileList is not null"); //Assert.notNull(authTypeMode,"authTypeMode is not null"); List<InputStream> inputStreamList = new ArrayList<>(); try { ChannelSftp channelSftp = (ChannelSftp) loginInfo.get("sftp"); try { channelSftp.cd(uploadPath); } catch (SftpException e) { log.error("SFTP器服务存放文件路径不存在,系统重新创建目录:" + uploadPath, e); // 目录不存在,则创建文件夹 String[] dirs = uploadPath.split("/"); String tempPath = ""; int index = 0; mkdirDir(channelSftp, dirs, tempPath, dirs.length, index); } for (File file : uploadFileList) { InputStream inputStream = new FileInputStream(file); channelSftp.put(inputStream, file.getName()); inputStreamList.add(inputStream); } log.info("上传文件成功!"); } catch (Exception e) { log.error(e.getMessage(), e); throw new xException("用户上传SFTP服务器文件异常 message:" + e.getMessage(), e); } finally { if (!CollectionUtils.isEmpty(inputStreamList)) { try { for (InputStream inputStream : inputStreamList) { inputStream.close(); } } catch (IOException e) { log.error(e.getMessage(), e); throw new xException("用户上传SFTP关闭流异常 message:" + e.getMessage(), e); } } //sftpLoginOut(Objects.requireNonNull(loginInfo)); } } public static void uploadFtpFile(String connectionType, String uploadPath, List<File> uploadFileList, HashMap<String, Object> hashMap, String serverCharset) { FTPClient ftpClient = null; //获得文件流 try { //登录 ftpClient = (FTPClient) hashMap.get(ftp); // 中文支持 log.info("[FTP] serverCharset: " + serverCharset); ftpClient.setCharset(serverCharset); ftpClient.setType(FTPClient.TYPE_BINARY); // 用被动模式传输,解决linux服务长时间等待,导致超时问题 if (ENTER_LOCAL_ACTIVE_MODE.equals(connectionType)) { ftpClient.setPassive(false); } else { ftpClient.setPassive(true); } // 设置默认超时时间 System.setProperty("ftp4j.timeout", String.valueOf(timeout)); try { try { //判断目录是否存在 ftpClient.changeDirectory(uploadPath); } catch (FTPException e) { log.error(e.getMessage(), e); if (FTPCodes.FILE_NOT_FOUND == e.getCode()) { log.error("FTP目录" + uploadPath + "不存在"); //判断文件是否存在 ftpClient.fileSize(uploadPath); } else { throw new FTPException(e.getCode(), e.getMessage()); } } } catch (FTPException e) { log.error(e.getMessage(), e); if (FTPCodes.FILE_NOT_FOUND == e.getCode()) { log.error("FTP文件" + uploadPath + "不存在"); } } //上传至Ftp for (File uploadFile : uploadFileList) { ftpClient.upload(uploadFile); log.info("文件发送成功:" + uploadFile.getName()); } } catch (Exception e) { log.error("ftp文件上传失败" + e.getMessage(), e); throw new xException("上传ftp失败 message:" + e.getMessage(), e); } finally { //关闭连接 // ftpLoginOut(Objects.requireNonNull(hashMap)); } } /** * ftp上传文件 * * @param connectionType * @param uploadPath 文件存入FTP 的路径 * @return 成功返回true 失败返回false * @throws SocketException * @throws IOException */ public static void uploadFtpFile(String connectionType, String uploadPath, List<File> uploadFileList, HashMap<String, Object> hashMap) { log.info("默认FTP上传方法: defaultFtpEncode"); uploadFtpFile(connectionType, uploadPath, uploadFileList, hashMap, defaultFtpEncode); } /******************************************** 删除文件 *****************************************************/ /** * 删除FTP * * @param ftpName ftp上的文件名 * @param uploadPath ftp上的文件路径 * @return 成功返回true 失败返回false * @throws SocketException * @throws IOException */ public static Boolean deleteFtpFile(String uploadPath, String ftpName, HashMap<String, Object> hashMap) { Boolean flag; FTPClient ftpClient; //保存至Ftp try { ftpClient = (FTPClient) hashMap.get(ftp); System.setProperty("ftp4j.timeout", String.valueOf(timeout)); ftpClient.deleteFile(ftpName); } catch (Exception e) { log.error(e.getMessage(), e); throw new xException("删除ftp失败 message:" + e.getMessage(), e); } finally { //关闭连接 ftpLoginOut(Objects.requireNonNull(hashMap)); } return true; } /** * 删除sftp文件 * * @param directory 要删除文件所在目录 * @param deleteFile 要删除的文件 /x/xx/xx/myfile.txt * @throws Exception */ public static Boolean deleteSftpFile(String directory, String deleteFile, HashMap<String, Object> hashMap) { Boolean flag = true; try { ChannelSftp channelSftp = (ChannelSftp) hashMap.get(sftp); channelSftp.cd(directory); channelSftp.rm(deleteFile); } catch (Exception e) { log.error(e.getMessage(), e); throw new xException("删除sftp失败 message:" + e.getMessage(), e); } finally { //关闭连接 sftpLoginOut(Objects.requireNonNull(hashMap)); } return flag; } /**********************************************下载文件**************************************************************/ /** * 下载FTP * * @param ftpName ftp上的文件名 * @param localFile 保存的本地地址 * @param path ftp上的文件路径 * @return 成功返回true 失败返回false * @throws SocketException * @throws IOException */ public static String FTPDownloadFile(String path, String ftpName, File localFile, FtpSendVO ftpSendVO) { boolean flag = true; //保存至Ftp FTPClient ftpClient = new FTPClient();// ftpHost为FTP服务器的IP地址,port为FTP服务器的登陆端口,ftpHost为String型,port为int型。 try { connectFTP(ftpSendVO, null); ftpClient.download(ftpName, localFile); log.info("FTP文件名——{}", ftpName); } catch (Exception e) { log.error(e.getMessage(), e); return "SocketNotSuccess"; } finally { //关闭连接 try { ftpClient.disconnect(true); } catch (Exception e) { log.error(e.getMessage(), e); } } return "SUCCESS"; } /** * 下载文件 * * @param downloadFilePath 要下载的文件所在绝对路径 * @param downloadFileName 要下载的文件名(sftp服务器上的文件名) * @param loginInfo 用户认证方式 */ public static void SFTPDownload(String downloadFilePath, String downloadFileName, String saveFile, HashMap<String, Object> loginInfo) throws Exception { Assert.notNull(downloadFilePath, "downloadFilePath is not null"); Assert.notNull(downloadFileName, "downloadFileName is not null"); Assert.notNull(saveFile, "saveFile is not null"); Assert.notNull(loginInfo, "loginInfo is not null"); OutputStream outputStream = null; ChannelSftp sftp; try { sftp = (ChannelSftp) loginInfo.get("sftp"); sftp.cd(downloadFilePath); sftp.ls(downloadFileName); outputStream = new FileOutputStream(saveFile); sftp.get(downloadFileName, outputStream); log.info("文件下载完成!"); } finally { if (null != outputStream) { try { outputStream.close(); } catch (Exception e) { log.error(e.getMessage(), e); } } sftpLoginOut(loginInfo); } } /****************************** 获取文件的大小 *****************************************/ /** * ftp获取文件的大小 * * @param hashMap * @return */ public static FileMateData ftpFolderSize(String directoryPath, String fileName, HashMap<String, Object> hashMap) { if (StringUtils.isEmpty(fileName)) { throw new xException("文件名不能为空"); } FileMateData fileMateData = new FileMateData(); try { FTPClient client = (FTPClient) hashMap.get(ftp); //查看当前目录 String workingDirectory = client.currentDirectory(); log.info("当前目录: {}", workingDirectory); //获取指定目录下的文件及目录 fileMateData.setSize(client.fileSize(directoryPath) / 1000 + "KB"); fileMateData.setFileName(fileName); } catch (Exception e) { log.error(e.getMessage(), e); } return fileMateData; } public static FileMateData sftpFolderSize(String directoryPath, String fileName, HashMap<String, Object> hashMap) { ChannelSftp channelSftp = (ChannelSftp) hashMap.get(sftp); long size = 0L; FileMateData fileMateData = new FileMateData(); try { Vector list = channelSftp.ls(directoryPath); if (list.isEmpty()) { throw new xException(fileName + " 文件不存在"); } for (Object sftpFile : list) { ChannelSftp.LsEntry lsEntry = (ChannelSftp.LsEntry) sftpFile; String nextName = lsEntry.getFilename(); if (nextName.equals(fileName)) { SftpATTRS attrs = lsEntry.getAttrs(); size = attrs.getSize(); fileMateData.setSize(size / 1000 + "KB"); fileMateData.setFileName(fileName); } } } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new xException(fileName + " 文件异常: " + ex.getMessage(), ex); } return fileMateData; } /*******************************************临时文件生成方法*******************************************/ /** * 生成文件 * * @param fileName * @param sshKey * @param temporaryPath 临时文件保存路径 * @return */ public static File createFile(String fileName, String sshKey, String temporaryPath) { // SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); //String extFile = sdf.format(new Date()) + UUID.randomUUID().toString().replace("-", ""); return writerFile(fileName, sshKey, temporaryPath); } /** * 创建文件 */ public static File writerFile(String fileName, String sshKey, String temporaryPath) { File file = null; // 生成格式文件 Writer write = null; try { file = new File(temporaryPath); if (!file.exists()) { file.mkdir(); } // 保证创建一个新文件 file = new File(temporaryPath + fileName); log.info("filename: " + file.getAbsolutePath()); // 如果已存在,删除旧文件 if (file.exists()) { file.delete(); } file.createNewFile(); // 将格式化后的字符串写入文件 write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); write.write(sshKey); write.flush(); } catch (Exception e) { log.error("生成文件异常: " + e.getMessage(), e); } finally { if (write != null) { try { write.close(); } catch (IOException e) { log.error(e.getMessage(), e); } } } // 返回文件对象 return file; } /** * 递归根据路径创建文件夹 * * @param dirs 根据 / 分隔后的数组文件夹名称 * @param tempPath 拼接路径 * @param length 文件夹的格式 * @param index 数组下标 * @return */ public static void mkdirDir(ChannelSftp channelSftp, String[] dirs, String tempPath, int length, int index) { // 以"/a/b/c/d"为例按"/"分隔后,第0位是"";顾下标从1开始 index++; if (index < length) { // 目录不存在,则创建文件夹 tempPath += "/" + dirs[index]; } try { log.info("检测目录[" + tempPath + "]"); channelSftp.cd(tempPath); if (index < length) { mkdirDir(channelSftp, dirs, tempPath, length, index); } } catch (SftpException ex) { log.error("创建目录[" + tempPath + "失败" + ex.getMessage(), ex); try { channelSftp.mkdir(tempPath); channelSftp.cd(tempPath); } catch (SftpException e) { log.error("创建目录[" + tempPath + "]失败,异常信息[" + e.getMessage() + "]", e); throw new RuntimeException("创建目录[" + tempPath + "]失败,异常信息[" + e.getMessage() + "]", e); } log.info("进入目录[" + tempPath + "]"); mkdirDir(channelSftp, dirs, tempPath, length, index); } }
}