CuteFTPUtils
对FTPClient的二次封装;
注意:FTPClient 有主目录的。
打个比方, FTPClient1 处于 a/b/c 的c目录下,则FTPClient1不能对a 目录下的文件操作。得切换主目录到a目录下,
或者新建另一个FTPClient2 对象。
方法摘要 | |
static CuteFTPUtils | getInstance() 创建并返回此对象,并登录 |
boolean | checkLoginState() 检测FtpClient登录状态 |
boolean | login() 登录FtpClient |
Function<FtpFileInfo, Boolean> | onUploadFileAfter() 上传后触发 |
boolean | uploadFile(String localFileName, String ftpDirName, String ftpFileName) 文件上传 |
boolean | uploadFile(String localFileName, String ftpDirName, String ftpFileName, boolean deleteLocalFile) 文件上传;是否删除本地文件 |
boolean | uploadFile(FileInputStream uploadInputStream, String ftpDirName, String ftpFileName) 文件上传;传入文件流 |
InputStream | getInputStream (String ftpDirName, String ftpFileName) 得到ftp库内指定文件的文件流(用于下载) |
boolean | downloadFile(String ftpDirName, String ftpFileName, String localFileFullName) 下载文件到指定的本地文件库 |
boolean | existFile(String ftpDirName,String ftpFileName) 判断指定路径下是否存在该文件 |
FTPClient | getFTPClient () 返回一个FTPClient 对象(与登录时实例化的FTPClient是同一个对象) |
FTPFile[] | getFTPFile(String ftpDirName) 获得指定目录下的所有文件 |
boolean | removeFile(String ftpFileName) 删除单文件;完整路径+文件名+文件后缀名 |
boolean | removeFileByName(String ftpDirName , String ftpFileName) 删除文件名相同的多文件,无视后缀 |
boolean | removeFileAndDirByName(String path , String ftpFileName) 删除文件名相同的 文件+文件夹 |
boolean | removeDir(String dir) 删除空目录 |
boolean | removeDirectoryALLFile(String pathName,boolean directory) 清空文件夹 / 删除文件夹 |
boolean | createDir(String dir) 创建目录 |
private void | closeFtpConnection() 销毁ftp连接 |
void | close() 销毁ftp连接,释放对象保存的资源(如打开的文件) |
static | combine(String... args) 合并路径 |
源码


1 import java.io.Closeable; 2 import java.io.File; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.util.function.Function; 8 9 import org.apache.commons.lang3.StringUtils; 10 import org.apache.commons.net.ftp.FTPClient; 11 import org.apache.commons.net.ftp.FTPClientConfig; 12 import org.apache.commons.net.ftp.FTPFile; 13 import org.apache.commons.net.ftp.FTPReply; 14 import org.slf4j.Logger; 15 import org.slf4j.LoggerFactory; 16 17 /** 18 * ftp 上传下载公共类 19 * 20 * @author QGuo 21 * 22 */ 23 public class CuteFTPUtils implements Closeable { 24 private final Logger logger = LoggerFactory.getLogger(this.getClass()); 25 26 private FTPClient ftp = null; 27 boolean _isLogin = false; 28 29 public static CuteFTPUtils getInstance() { 30 CuteFTPUtils cuteFTPUtils = new CuteFTPUtils(); 31 cuteFTPUtils.login(); 32 return cuteFTPUtils; 33 } 34 35 //判断当前ftp链接状态 36 public boolean checkLoginState() { 37 int reply = ftp.getReplyCode(); 38 if (!FTPReply.isPositiveCompletion(reply)) { 39 System.err.println("FTP服务器拒绝连接 "); 40 return false; 41 } else { 42 return true; 43 } 44 45 } 46 47 /** 48 * 49 * 登录 50 */ 51 public boolean login() { 52 ftp = new FTPClient(); 53 String hostname = PropertyUtil.getProperty("FTP_HOSTNAME"); 54 int port = Integer.valueOf(PropertyUtil.getProperty("FTP_PORT")); 55 String username = PropertyUtil.getProperty("FTP_USERNAME"); 56 String password = PropertyUtil.getProperty("FTP_PASSWORD"); 57 try { 58 // 连接 59 ftp.connect(hostname, port); 60 _isLogin = ftp.login(username, password); 61 logger.info(_isLogin ? "登录成功" : "登录失败"); 62 // 检测连接是否成功 63 int reply = ftp.getReplyCode(); 64 if (!FTPReply.isPositiveCompletion(reply)) { 65 System.err.println("FTP服务器拒绝连接 "); 66 return false; 67 } 68 return true; 69 } catch (Exception ex) { 70 ex.printStackTrace(); 71 return false; 72 } 73 } 74 75 /** 76 * 上传后触发 77 */ 78 public Function<FtpFileInfo, Boolean> onUploadFileAfter; 79 80 /** 81 * 82 * ftp上传文件 83 * 84 * @param localFileName 85 * 待上传文件 86 * @param ftpDirName 87 * ftp 目录名 88 * @param ftpFileName 89 * ftp目标文件 90 * @return true||false 91 */ 92 public boolean uploadFile(String localFileName, String ftpDirName, String ftpFileName) { 93 return uploadFile(localFileName, ftpDirName, ftpFileName, false); 94 } 95 96 /** 97 * 98 * ftp上传文件 99 * 100 * @param localFileName 101 * 待上传文件 102 * @param ftpDirName 103 * ftp 目录名 104 * @param ftpFileName 105 * ftp目标文件 106 * @param deleteLocalFile 107 * 是否删除本地文件 108 * @return true||false 109 */ 110 public boolean uploadFile(String localFileName, String ftpDirName, String ftpFileName, boolean deleteLocalFile) { 111 112 printFormat("准备上传 [{0}] 到 ftp://{1}/{2}", localFileName, ftpDirName, ftpFileName); 113 if (StringUtils.isEmpty(ftpFileName)) 114 throw new RuntimeException("上传文件必须填写文件名!"); 115 116 File srcFile = new File(localFileName); 117 if (!srcFile.exists()) 118 throw new RuntimeException("文件不存在:" + localFileName); 119 120 try (FileInputStream fis = new FileInputStream(srcFile)) { 121 // 上传文件 122 boolean flag = uploadFile(fis, ftpDirName, ftpFileName); 123 // 上传前事件 124 if (onUploadFileAfter != null) { 125 onUploadFileAfter.apply(new FtpFileInfo(localFileName, ftpDirName, ftpFileName)); 126 } 127 // 删除文件 128 if (deleteLocalFile) { 129 srcFile.delete(); 130 printFormat("ftp删除源文件:{0}", srcFile); 131 } 132 fis.close(); 133 return flag; 134 } catch (Exception e) { 135 e.printStackTrace(); 136 return false; 137 } finally { 138 } 139 } 140 141 /** 142 * 143 * ftp上传文件 (使用inputstream) 144 * 145 * @param localFileName 146 * 待上传文件 147 * @param ftpDirName 148 * ftp 目录名 149 * @param ftpFileName 150 * ftp目标文件 151 * @return true||false 152 */ 153 public boolean uploadFile(FileInputStream uploadInputStream, String ftpDirName, String ftpFileName) { 154 printFormat("准备上传 [流] 到 ftp://{0}/{1}", ftpDirName, ftpFileName); 155 // if(StringExtend.isNullOrEmpty(ftpDirName)) 156 // ftpDirName="/"; 157 if (StringUtils.isEmpty(ftpFileName)) 158 throw new RuntimeException("上传文件必须填写文件名!"); 159 160 try { 161 // 设置上传目录(没有则创建) 162 if (!createDir(ftpDirName)) { 163 throw new RuntimeException("切入FTP目录失败:" + ftpDirName); 164 } 165 ftp.setBufferSize(1024); 166 // 解决上传中文 txt 文件乱码 167 ftp.setControlEncoding("GBK"); 168 FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT); 169 conf.setServerLanguageCode("zh"); 170 171 // 设置文件类型(二进制) 172 ftp.setFileType(FTPClient.BINARY_FILE_TYPE); 173 // 上传 174 String fileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1"); 175 if (this.ftp.storeFile(fileName, uploadInputStream)) { 176 uploadInputStream.close(); 177 printFormat("文件上传成功:{0}/{1}", ftpDirName, ftpFileName); 178 return true; 179 } 180 181 return false; 182 } catch (Exception e) { 183 e.printStackTrace(); 184 return false; 185 } finally { 186 } 187 } 188 189 /** 190 * 获取ftp上文件,得到该文件流 191 * @param ftpDirName 目录 192 * @param ftpFileName 文件名称(带后缀) 193 * @return 194 */ 195 public InputStream getInputStream (String ftpDirName, String ftpFileName) { 196 try { 197 if ("".equals(ftpDirName)) 198 ftpDirName = "/"; 199 String dir = new String(ftpDirName.getBytes("GBK"), "iso-8859-1"); 200 if (!ftp.changeWorkingDirectory(dir)) { 201 System.out.println("切换目录失败:" + ftpDirName); 202 } 203 FTPFile[] fs = ftp.listFiles(); 204 String fileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1"); 205 for (FTPFile ff : fs) { 206 if (ff.getName().equals(fileName)) { 207 System.out.println("成功获取ftp文件流:" + ftpFileName + ";目录:" + ftpDirName); 208 return ftp.retrieveFileStream(ftpFileName); 209 } 210 } 211 System.out.println("获取ftp文件流,失败:" + ftpFileName + ";目录:" + ftpDirName); 212 } catch (Exception e) { 213 e.printStackTrace(); 214 } 215 return null; 216 217 } 218 219 /** 220 * 下载文件 221 * 222 * @param ftpDirName 223 * ftp目录名 224 * @param ftpFileName 225 * ftp文件名 226 * @param localFileFullName 227 * 本地文件名 228 */ 229 public boolean downloadFile(String ftpDirName, String ftpFileName, String localFileFullName) { 230 try { 231 if ("".equals(ftpDirName)) 232 ftpDirName = "/"; 233 String dir = new String(ftpDirName.getBytes("GBK"), "iso-8859-1"); 234 if (!ftp.changeWorkingDirectory(dir)) { 235 System.out.println("切换目录失败:" + ftpDirName); 236 return false; 237 } 238 FTPFile[] fs = ftp.listFiles(); 239 String fileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1"); 240 for (FTPFile ff : fs) { 241 if (ff.getName().equals(fileName)) { 242 FileOutputStream is = new FileOutputStream(new File(localFileFullName)); 243 ftp.retrieveFile(ff.getName(), is); 244 is.close(); 245 System.out.println("下载ftp文件,已下载:" + localFileFullName); 246 return true; 247 } 248 } 249 System.out.println("下载ftp文件,失败:" + ftpFileName + ";目录:" + ftpDirName); 250 return false; 251 } catch (Exception e) { 252 e.printStackTrace(); 253 return false; 254 } 255 } 256 257 /** 258 * 判断指定路径下是否存在改文件 259 * @param ftpDirName 260 * @param ftpFileName 261 * @return 262 */ 263 public boolean existFile(String ftpDirName,String ftpFileName) { 264 FTPFile[] fileArray = null; 265 FTPFile file = null; 266 try { 267 fileArray = ftp.listFiles(ftpDirName); 268 } catch (IOException e) { 269 // TODO Auto-generated catch block 270 e.printStackTrace(); 271 } 272 for (int i = 0; i < fileArray.length; i++) { 273 file = fileArray[i]; 274 //确认该file 为 文件 275 if (file.isFile()) { 276 //当字段相等时,确认存在 277 if (ftpFileName.equals(file.getName())) { 278 return true; 279 } 280 } 281 } 282 return false; 283 } 284 285 /** 286 * 返回FTPClient 对象 287 * @return 288 */ 289 public FTPClient getFTPClient () { 290 return this.ftp; 291 } 292 293 /** 294 * 获得指定目录下的所有文件 295 * @param ftpDirName 296 * @return 297 */ 298 public FTPFile[] getFTPFile(String ftpDirName) { 299 FTPFile[] ftpFile = null ; 300 try { 301 ftpFile = ftp.listFiles(ftpDirName); 302 } catch (IOException e) { 303 // TODO Auto-generated catch block 304 e.printStackTrace(); 305 } 306 307 return ftpFile; 308 } 309 310 /** 311 * 312 * 删除ftp上的文件(完整路径,包括文件后缀名) 313 * @param ftpFileName 314 * @return true || false 315 */ 316 public boolean removeFile(String ftpFileName) { 317 boolean flag = false; 318 printFormat("待删除文件:{0}", ftpFileName); 319 try { 320 ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1"); 321 flag = ftp.deleteFile(ftpFileName); 322 printFormat("删除文件:[{0}]", flag ? "成功" : "失败"); 323 return flag; 324 } catch (IOException e) { 325 e.printStackTrace(); 326 return false; 327 } 328 } 329 330 /** 331 * 删除库中,所有文件名相同的文件(路径+文件名)不用后缀名 332 * @param path 文件路径 333 * @param ftpFileName 不带后缀的文件名 334 * @return 335 */ 336 public boolean removeFileByName(String ftpDirName , String ftpFileName) { 337 boolean flag = false; 338 FTPFile[] tempFile; 339 try { 340 tempFile = ftp.listFiles(ftpDirName); 341 for (int i = 0; i < tempFile.length; i++) { 342 String fileName = null ; 343 //只删除文件,文件夹不处理 344 if (tempFile[i].isFile()) { 345 fileName = tempFile[i].getName(); 346 } else { 347 continue; 348 } 349 if (ftpFileName.equals(fileName.substring(0, fileName.lastIndexOf(".")))) { 350 System.out.println("将被删除的文件名:" + fileName); 351 try { 352 ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1"); 353 flag = ftp.deleteFile(ftpDirName+"/"+tempFile[i].getName()); 354 System.out.println("文件" + fileName + "删除成功"); 355 } catch (IOException e) { 356 // TODO Auto-generated catch block 357 System.out.println("文件" + fileName + "删除失败"); 358 e.printStackTrace(); 359 }; 360 } 361 } 362 } catch (IOException e1) { 363 e1.printStackTrace(); 364 System.out.println(e1.getMessage()); 365 } 366 return flag; 367 } 368 369 /** 370 * 删除库中,所有文件名相同的文件(路径+文件名)不用后缀名; 371 * 包括同名的文件夹 372 * @param path 文件路径 373 * @param ftpFileName 不带后缀的文件名 374 * @return 375 */ 376 public boolean removeFileAndDirByName(String path , String ftpFileName) { 377 boolean flag = false; 378 FTPFile[] tempFile; 379 try { 380 tempFile = ftp.listFiles(path); 381 for (int i = 0; i < tempFile.length; i++) { 382 String fileName = null ; 383 //只删除文件,文件夹不处理 384 if (tempFile[i].isFile()) { 385 fileName = tempFile[i].getName(); 386 } else if (tempFile[i].isDirectory()){ 387 //删除文件夹 388 removeDirectoryALLFile(path+"/"+tempFile[i].getName(),true); 389 } else { 390 continue; 391 } 392 393 if (ftpFileName.equals(fileName.substring(0, fileName.lastIndexOf(".")))) { 394 System.out.println("将被删除的文件名:" + fileName); 395 try { 396 ftpFileName = new String(ftpFileName.getBytes("GBK"), "iso-8859-1"); 397 flag = ftp.deleteFile(path+"/"+tempFile[i].getName()); 398 System.out.println("文件" + fileName + "删除成功"); 399 } catch (IOException e) { 400 // TODO Auto-generated catch block 401 System.out.println("文件" + fileName + "删除失败"); 402 e.printStackTrace(); 403 }; 404 } 405 } 406 } catch (IOException e1) { 407 e1.printStackTrace(); 408 System.out.println(e1.getMessage()); 409 } 410 return flag; 411 } 412 413 /** 414 * 删除空目录 415 * 416 * @param dir 417 * @return 418 */ 419 public boolean removeDir(String dir) { 420 if (dir.startsWith("/")) 421 dir = "/" + dir; 422 try { 423 String d = new String(dir.toString().getBytes("GBK"), "iso-8859-1"); 424 return ftp.removeDirectory(d); 425 } catch (Exception e) { 426 e.printStackTrace(); 427 return false; 428 } 429 } 430 431 /** 432 * 删除Ftp上的文件夹(可控制是否保留文件夹) 包括其中的文件 433 * 注:① 删除文件夹,请携带 "/" ② false亦保留子文件夹 434 * @param directory ture 删除文件夹 false保留文件夹 435 * @param pathName 文件夹路径 436 * @return 是否成功 437 */ 438 public boolean removeDirectoryALLFile(String pathName,boolean directory){ 439 try 440 { 441 FTPFile[] files = ftp.listFiles(pathName); 442 if (null != files && files.length > 0) 443 { 444 for (FTPFile file : files) 445 { 446 if (file.isDirectory()) 447 { 448 removeDirectoryALLFile(pathName + "/" + file.getName(),directory); 449 // 切换到父目录,不然删不掉文件夹 450 ftp.changeWorkingDirectory(pathName.substring(0, pathName.lastIndexOf("/"))); 451 ftp.removeDirectory(pathName); 452 } 453 else 454 { 455 if (!ftp.deleteFile(pathName + "/" + file.getName())) 456 { 457 System.out.println("删除指定文件" + pathName + "/" + file.getName() + "失败!"); 458 return false; 459 } 460 } 461 } 462 } 463 // 切换到父目录,不然删不掉文件夹 464 ftp.changeWorkingDirectory(pathName.substring(0, pathName.lastIndexOf("/"))); 465 if (directory) 466 ftp.removeDirectory(pathName); 467 } 468 catch (IOException e) 469 { 470 logger.error("删除指定文件夹" + pathName + "失败:" + e); 471 e.printStackTrace(); 472 return false; 473 } 474 475 return true; 476 } 477 478 /** 479 * 创建目录(有则切换目录,没有则创建目录) 480 * 481 * @param dir 482 * @return 483 */ 484 public boolean createDir(String dir) { 485 if (StringUtils.isEmpty(dir)) 486 return true; 487 String d; 488 try { 489 // 目录编码,解决中文路径问题 490 d = new String(dir.toString().getBytes("GBK"), "iso-8859-1"); 491 // 尝试切入目录 492 if (ftp.changeWorkingDirectory(d)) 493 return true; 494 dir = trimStart(dir, "/"); 495 dir = trimEnd(dir, "/"); 496 String[] arr = dir.split("/"); 497 StringBuffer sbfDir = new StringBuffer(); 498 // 循环生成子目录 499 for (String s : arr) { 500 sbfDir.append("/"); 501 sbfDir.append(s); 502 // 目录编码,解决中文路径问题 503 d = new String(sbfDir.toString().getBytes("GBK"), "iso-8859-1"); 504 // 尝试切入目录 505 if (ftp.changeWorkingDirectory(d)) 506 continue; 507 if (!ftp.makeDirectory(d)) { 508 System.out.println("[失败]ftp创建目录:" + sbfDir.toString()); 509 return false; 510 } 511 System.out.println("[成功]创建ftp目录:" + sbfDir.toString()); 512 } 513 // 将目录切换至指定路径 514 return ftp.changeWorkingDirectory(d); 515 } catch (Exception e) { 516 e.printStackTrace(); 517 return false; 518 } 519 } 520 521 /** 522 * 523 * 销毁ftp连接 524 * 525 */ 526 private void closeFtpConnection() { 527 _isLogin = false; 528 if (ftp != null) { 529 if (ftp.isConnected()) { 530 try { 531 ftp.logout(); 532 ftp.disconnect(); 533 logger.info("ftp:退出"); 534 } catch (IOException e) { 535 e.printStackTrace(); 536 } 537 } 538 } 539 } 540 541 /** 542 * 543 * 销毁ftp连接 544 * 545 */ 546 @Override 547 public void close() { 548 this.closeFtpConnection(); 549 } 550 551 public static class FtpFileInfo { 552 public FtpFileInfo(String srcFile, String ftpDirName, String ftpFileName) { 553 this.ftpDirName = ftpDirName; 554 this.ftpFileName = ftpFileName; 555 this.srcFile = srcFile; 556 } 557 558 String srcFile; 559 String ftpDirName; 560 String ftpFileName; 561 String ftpFileFullName; 562 563 public String getSrcFile() { 564 return srcFile; 565 } 566 567 public void setSrcFile(String srcFile) { 568 this.srcFile = srcFile; 569 } 570 571 public String getFtpDirName() { 572 return ftpDirName; 573 } 574 575 public void setFtpDirName(String ftpDirName) { 576 this.ftpDirName = ftpDirName; 577 } 578 579 public String getFtpFileName() { 580 return ftpFileName; 581 } 582 583 public void setFtpFileName(String ftpFileName) { 584 this.ftpFileName = ftpFileName; 585 } 586 587 /** 588 * 获取ftp上传文件的完整路径名 589 * @return 590 */ 591 public String getFtpFileFullName() { 592 return combine("/", ftpDirName, ftpFileName); 593 } 594 595 } 596 597 /** 598 * 删除起始字符 599 * 600 * @param s 601 * @return 602 */ 603 public static String trimStart(String str, String trim) { 604 if (str == null) 605 return null; 606 return str.replaceAll("^(" + trim + ")+", ""); 607 } 608 609 /** 610 * 删除末尾字符 611 * 612 * @param s 613 * @return 614 */ 615 public static String trimEnd(String str, String trim) { 616 if (str == null) 617 return null; 618 return str.replaceAll("(" + trim + ")+$", ""); 619 } 620 621 /** 622 * 格式化输出,打印信息到控制台 623 * 624 * @param format 625 * @param args 626 */ 627 public static void printFormat(String format, Object... args) { 628 if (args == null) { 629 System.out.println(format); 630 } 631 System.out.println(java.text.MessageFormat.format(format, args)); 632 } 633 634 /** 635 * 合并路径 636 * 637 * @param args 638 * @return 639 */ 640 public static String combine(String... args) { 641 if (args == null || args.length == 0) 642 return ""; 643 StringBuffer sbf = new StringBuffer(); 644 for (String s : args) { 645 // 首位地址只删除尾部正反斜杠 646 if (sbf.length() == 0) { 647 sbf.append(s.replaceAll("/{1,}$|\\{1,}$", "")); 648 continue; 649 } 650 651 if (sbf.length() > 0) 652 sbf.append("/"); 653 // 去除首尾正反斜杠 654 sbf.append(s.replaceAll("^/{1,}|^\\{1,}", "").replaceAll("/{1,}$|\\{1,}$", "")); 655 } 656 657 return sbf.toString(); 658 } 659 660 }