java中ftpClient.listFiles()结果为空问题解决方案

时间:2021-10-27 13:13:08

最近在JAVA项目中用到ftpClient.listFiles()函数时,总是返回null。现在就我遇到的情况讲讲解决方案。

环境:CentOS release 6.6 (Final), 语言 $LANG=en_US.UTF-8

相关jar:common-net-3.3.jar(common-net-3.4.jar和common-net-1.4.1.jar依旧有这个问题)


之前查了一些资料,别人在处理这个问题时,常常是在执行ftpClient.listFiles()前加上一句:

ftpClient.enterLocalPassiveMode();

也有人说是因为目标机器是中文语言设置的问题,在匹配文件信息时无法匹配中文日期的“月”和“日”而出错。关于这个问题,大家可以参考这位朋友的方法:

http://blog.csdn.net/wangchsh2008/article/details/47101423 

但是这对我依然不适用!不过却给我提供了解决问题的思路。我明白了原来这个函数会匹配 ls -l的结果

在我的目标机器上:

$ ls -l 
total 4
-rw-rw-r--. 1 tester5 tester5 7 Apr 28 16:44 test.txt

大家注意看,在“-rw-rw-r--” 后面,紧跟着一个不起眼的“.”,而这个小小的点正是函数返回Null的元凶!

这个点不是一定会出现的。在另外一台机器上,同样执行ls -l的结果:

$ ls -l 

total 7984

-rwxrwxrwx. 1 root root     135 Sep 23  2015 py_test.pl

-rwxrwxrwx  1 root root    3897 Apr   8 15:35 test.pl

大家就可以看到,第二个文件test.pl的“-rwxrwxrwx”后面并没有那个“."。所以不同的文件执行ls -l的结果形式可能不同(具体原因是什么,我还没有去研究)。

在已有的jar包common-net-3.3.jar,common-net-3.4.jarcommon-net-1.4.1.jar源码中,都没有对这个“点”做正则匹配。

下面的代码是common-net-1.4.1.jar中UnixFTPEntryParser.java中的相关代码:

 /** 
   * this is the regular expression used by this parser. 
   * 
   * Permissions: 
   *    r   the file is readable 
   *    w   the file is writable 
   *    x   the file is executable 
   *    -   the indicated permission is not granted 
   *    L   mandatory locking occurs during access (the set-group-ID bit is 
   *        on and the group execution bit is off) 
   *    s   the set-user-ID or set-group-ID bit is on, and the corresponding 
   *        user or group execution bit is also on 
   *    S   undefined bit-state (the set-user-ID bit is on and the user 
   *        execution bit is off) 
   *    t   the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and 
   *        execution is on 
   *    T   the 1000 bit is turned on, and execution is off (undefined bit- 
   *        state) 
   */  
  private static final String REGEX =  
      "([bcdlfmpSs-])"  
      +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"  //这里没有考虑到那个"."的问题!!!
      + "(\\d+)\\s+"  
      + "(\\S+)\\s+"  
      + "(?:(\\S+)\\s+)?"  
      + "(\\d+)\\s+"  
        
      /* 
        numeric or standard format date 
      */  
      + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+" //这句对于中文语言设置的系统会发生匹配问题。解决方法见上文我推荐的那篇博客
  
      /*  
         year (for non-recent standard format)  
   or time (for numeric or recent standard format   
*/  
+ "(\\d+(?::\\d+)?)\\s+"  
        
+ "(\\S*)(\\s*.*)"; 

那么,我们只要重写这个方法即可。把对于“."的匹配加上。
    /**
     * this is the regular expression used by this parser.
     *
     * Permissions:
     *    r   the file is readable
     *    w   the file is writable
     *    x   the file is executable
     *    -   the indicated permission is not granted
     *    L   mandatory locking occurs during access (the set-group-ID bit is
     *        on and the group execution bit is off)
     *    s   the set-user-ID or set-group-ID bit is on, and the corresponding
     *        user or group execution bit is also on
     *    S   undefined bit-state (the set-user-ID bit is on and the user
     *        execution bit is off)
     *    t   the 1000 (octal) bit, or sticky bit, is on [see chmod(1)], and
     *        execution is on
     *    T   the 1000 bit is turned on, and execution is off (undefined bit-
     *        state)
     */
    private static final String REGEX =
        "([bcdlfmpSs-])"
        //+"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"
            //To match the "." of "-rw-rw-rw-."   @author Sun Ying
        +"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\.*\\s+"
        + "(\\d+)\\s+"
        + "(\\S+)\\s+"
        + "(?:(\\S+)\\s+)?"
        + "(\\d+)\\s+"
        
        /*
          numeric or standard format date
        */
        //中文匹配问题出在此处,这个匹配只匹配2中形式:
        //(1)2008-08-03
        //(2)(Jan  9)  或   (4月 26)
        //而出错的hp机器下的显示为 8月20日(没有空格分开)
        //故无法匹配而报错
        //将下面字符串改为:
        + "((?:\\S+\\s+\\S+)|(?:\\S+))\\s+"
        //+ "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+)|(?:\\S+))\\s+"
        //上面这句是原来那位博主改的,但是第三部分(?:\\S+)已经包含了第一部分(?:\\d+[-/]\\d+[-/]\\d+),
        //所以我去掉了第一部分.  @author Sun Ying
        //+ "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"

        /* 
           year (for non-recent standard format) 
                 or time (for numeric or recent standard format  
                 */
            + "(\\d+(?::\\d+)?)\\s+"
        
            + "(\\S*)(\\s*.*)";

(如果上述粘贴的代码中有<span style="white-space:pre">    </span> 这种东西,请手动去掉。我不知道为什么粘贴的代码会莫名其妙冒出这种东西。删了好几次,还是会自动出来。)


另一个文件FTPTimestampParserImplExZH.java是前面那位博主朋友改的,上面的文件需要配合这个文件使用,才能解决由中文语言导致的返回值为Null的问题。

这两个文件可以到如下地址下载: http://download.csdn.net/detail/yingprince/9506352 


将上面的2个文件加入java项目后,用如下的方法调用:

ftpClient.changeWorkingDirectory(path);
ftpClient.enterLocalPassiveMode();
ftpClient.configure(new FTPClientConfig("cn.com.wechat.ftp.UnixFTPEntryParser")); //这里记得改成你放的位置
FTPFile[] fs = ftpClient.listFiles(); // 得到目录的相应文件列表


上面2个文件用的源码虽然是common-net-1.4.1.jar的源码,但是用在common-net-3.3.jar和common-net-3.4.jar也是可行的。我已经实际验证过了。


如有什么问题,还请大家指正。

----------------------------分割线---------------------------------------

看到有人反映自己用了有问题,我特意写了一个项目:http://download.csdn.net/download/yingprince/10149251 

这个项目里有完整的使用方法,有测试过程。

提醒一下,这个项目中有一处错误,FtpUtils.java第46行,

ftp.configure(new FTPClientConfig("com.ecample.ftp.UnixFTPEntryParser"));

应该改为:ftp.configure(new FTPClientConfig("com.example.ftp.UnixFTPEntryParser"));

不小心包名写错了,见谅。CSDN已上传的资源不能删也不能修改描述,真是坑!