最近在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.youkuaiyun.com/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.youkuaiyun.com/detail/yingprince/9506352
ftpClient.changeWorkingDirectory(path);
ftpClient.enterLocalPassiveMode();
ftpClient.configure(new FTPClientConfig("cn.com.wechat.ftp.UnixFTPEntryParser")); //这里记得改成你放的位置
FTPFile[] fs = ftpClient.listFiles(); // 得到目录的相应文件列表
如有什么问题,还请大家指正。
----------------------------分割线---------------------------------------
看到有人反映自己用了有问题,我特意写了一个项目:http://download.youkuaiyun.com/download/yingprince/10149251
这个项目里有完整的使用方法,有测试过程。
提醒一下,这个项目中有一处错误,FtpUtils.java第46行,
ftp.configure(new FTPClientConfig("com.ecample.ftp.UnixFTPEntryParser"));
应该改为:ftp.configure(new FTPClientConfig("com.example.ftp.UnixFTPEntryParser"));
不小心包名写错了,见谅。优快云已上传的资源不能删也不能修改描述,真是坑!