最近在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.jar和common-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
ftpClient.changeWorkingDirectory(path); ftpClient.enterLocalPassiveMode(); ftpClient.configure(new FTPClientConfig("cn.com.wechat.ftp.UnixFTPEntryParser")); //这里记得改成你放的位置 FTPFile[] fs = ftpClient.listFiles(); // 得到目录的相应文件列表
如有什么问题,还请大家指正。
----------------------------分割线---------------------------------------
看到有人反映自己用了有问题,我特意写了一个项目: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已上传的资源不能删也不能修改描述,真是坑!