最近在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也是可行的。我已经实际验证过了。


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