目前開發(fā)的這個項目中需要從遠程服務器上下載數(shù)據(jù),采用了開源的commons.net.ftp包。在實際應用中發(fā)現(xiàn)了一個問題,在測試服務器上調(diào)用ftpClient.listFiles()方法可以返回包含文件名的數(shù)組,而在現(xiàn)網(wǎng)服務器上此方法返回NULL。我被這個問題困擾了好久,下面把我的處理思路陳述如下:
(1)首先發(fā)現(xiàn)2個服務器的區(qū)別:測試服務器為solaris服務器,而現(xiàn)網(wǎng)服務器為hp服務器,會不會是平臺差異所致呢?帶著這個問題,下載了common包的源碼,通過源碼進行調(diào)試。
(2)FTPListParseEngine負責處理通過socket來獲取遠程服務器的信息。大概執(zhí)行了ls –l
操作,并把結(jié)果一行行放入一個linkedlist中。代碼如下:
1
private void readStream(InputStream stream, String encoding) throws IOException
2
{
3
BufferedReader reader;
4
if (encoding == null)
5
{
6
reader = new BufferedReader(new InputStreamReader(stream));
7
}
8
else
9
{
10
reader = new BufferedReader(new InputStreamReader(stream, encoding));
11
}
12
13
String line = this.parser.readNextEntry(reader);
14
15
while (line != null)
16
{
17
this.entries.add(line);
18
line = this.parser.readNextEntry(reader);
19
}
20
reader.close();
21
}
22
(3)這個時候發(fā)現(xiàn)問題了,傳入line中的字符串中有亂碼!正常的應該為:
drwxr-xr-x 11 daladmin daladmin 1024 2004年9月18日 mqm
|
其中時間那部分為亂碼。
(4)處理:在調(diào)用listFiles()之前先調(diào)用ftpClient.setControlEncoding("GBK");這樣line就能正常顯示了,但是listFiles() 返回依然為空!!! 繼續(xù).....
(5) 發(fā)現(xiàn)繼續(xù)運行的時候有一個正則表達式匹配不成功,代碼如下:
1
public boolean matches(String s)
2
{
3
this.result = null;
4
if (_matcher_.matches(s.trim(), this.pattern))
5
{
6
this.result = _matcher_.getMatch();
7
}
8
return null != this.result;
9
}
10
s即為(3)中的line,追蹤正則表達式,是在具體的子類UnixFTPEntryParser中寫死的。如下:
1
private static final String REGEX =
2
"([bcdlfmpSs-])"
3
+"(((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-]))((r|-)(w|-)([xsStTL-])))\\+?\\s+"
4
+ "(\\d+)\\s+"
5
+ "(\\S+)\\s+"
6
+ "(?:(\\S+)\\s+)?"
7
+ "(\\d+)\\s+"
8
9
/**//*
10
numeric or standard format date
11
*/
12
//問題出在此處,這個匹配只匹配2中形式:
13
//(1)2008-08-03
14
//(2)Jan 9或4月 26
15
//而出錯的hp機器下的顯示為 8月20日(沒有空格分開)
16
//故無法匹配而報錯
17
//將下面字符串改為:
18
//((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+)|(?:\\S+))\\s+
19
//便可以成功匹配
20
+ "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"
21
22
/**//*
23
year (for non-recent standard format)
24
or time (for numeric or recent standard format
25
*/
26
+ "(\\d+(?::\\d+)?)\\s+"
27
28
+ "(\\S*)(\\s*.*)";
29
(6)做上面修改后,能夠解析出來,但是接著又會報異常,錯誤發(fā)生在UnixFTPEntryParser類的parseFTPEntry方法中,common.net對中文支持的實在是不夠:
1
try
2
{
3
file.setTimestamp(super.parseTimestamp(datestr));
4
}
5
catch (ParseException e)
6
{
7
//注釋掉
8
return null; // this is a parsing failure too.
9
}
10
這個錯誤的原因是創(chuàng)建simpleDateFormat類時(詳情請見jdkAPI文檔)
public SimpleDateFormat(String pattern, Locale locale)
locale為EN,解決方案是創(chuàng)建一個新類,繼承ConfigurableFTPFileEntryParserImpl。其中的屬性defaultDateFormat和recentDateFormat 用Locale.CHINA初始化。而我目前的程序用不到取文件的修改時間,所以直接省事將上段代碼中的異常吞掉,即注釋掉return null 。網(wǎng)上有個解決方案(http://hi.baidu.com/hzwei206/blog/item/7c901d2debf7e136359bf7cd.html),是用了另一種方案,粘貼如下:
commons-net-1.4.1.jar包中ftp應用的幾點問題
一、異常:
從http://commons.apache.com網(wǎng)站下載了commons-net-1.4.1包后添加到自己的工程中,調(diào)用FtpClient類的listFiles(String pathName)方法時,拋如下異常:
Exception in thread "main" java.lang.NoClassDefFoundError :
org/apache/oro/text/regex/MalformedPatternException
at org.apache.commons.net.ftp.parser.RegexFTPFileEntryParserImpl.<init> (RegexFTPFileEntryParserImpl.java:75)
at org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl.<init>(ConfigurableFTPFileEntryParserImpl.java:57)
at org.apache.commons.net.ftp.parser.UnixFTPEntryParser.<init>(UnixFTPEntryParser.java:136)
at org.apache.commons.net.ftp.parser.UnixFTPEntryParser.<init>(UnixFTPEntryParser.java:119)
at org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory.createUnixFTPEntryParser(DefaultFTPFileEntryParserFactory.java:169)
at org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory.createFileEntryParser(DefaultFTPFileEntryParserFactory.java:94)
at org.apache.commons.net.ftp.FTPClient.initiateListParsing(FTPClient.java:2358)
at org.apache.commons.net.ftp.FTPClient.listFiles(FTPClient.java:2141)
at org.apache.commons.net.ftp.FTPClient.listFiles(FTPClient.java:2188)
.................
以上異常是由于缺少輔助的包jakarta-oro-2.0.8.jar引起的,去http://commons.apache.com網(wǎng)站下載該包后放入工程的lib下,并加載到classpath中,重新編譯運行,OK!
二、調(diào)用FtpClient類的listFiles(String pathName)方法失效的問題:
一般是由于ftp服務器(主要是小型機)的操作系統(tǒng)不同語言環(huán)境的時間格式造成的,在中文環(huán)境下,文件或文件夾的時間格式為"m月d日 hh:mm"或"yyyy年m月 d",而E文環(huán)境下時間格式為"MMM d yyyy"或"MMM d HH:mm",于是,在中文環(huán)境下,ftp包中的FTPTimestampParserImpl類將時間字符串Date化時拋異常,因為commons-net-1.4.1包不支持中文。
解決辦法(兩種辦法):
1. 將ftp服務器操作系統(tǒng)語言環(huán)境設為英文;
2. 修改ftp包的代碼:將FTPTimestampParserImpl類進行擴展,使之支持中文
下面針對第2種解決辦法來實現(xiàn):
(1) 新建類FTPTimestampParserImplExZH類:
1 /** *//**
2 * FTPTimestampParserImpl的擴展類,使之支持中文環(huán)境的時間格式
3 * Date:2007-8-15
4 */
5 package org.apache.commons.net.ftp.parser;
6
7 import java.text.ParseException;
8 import java.text.ParsePosition;
9 import java.text.SimpleDateFormat;
10 import java.util.Calendar;
11 import java.util.Date;
12
13 /** *//**
14 * @author hzwei206
15 * FTPTimestampParserImpl的擴展類,使之支持中文環(huán)境的時間格式
16 */
17 public class FTPTimestampParserImplExZH extends FTPTimestampParserImpl
18  {
19 private SimpleDateFormat defaultDateFormat = new SimpleDateFormat("mm d hh:mm");
20 private SimpleDateFormat recentDateFormat = new SimpleDateFormat("yyyy mm d");
21
22 /** *//**
23 * @author hzwei206
24 * 將中文環(huán)境的時間格式進行轉(zhuǎn)換
25 */
26 private String formatDate_Zh2En(String timeStrZh)
27 {
28 if (timeStrZh == null)
29 {
30 return "";
31 }
32
33 int len = timeStrZh.length();
34 StringBuffer sb = new StringBuffer(len);
35 char ch = ' ';
36 for (int i = 0;i < len;i++)
37 {
38 ch = timeStrZh.charAt(i);
39 if ((ch >= '0' && ch <= '9') || ch == ' ' || ch == ':')
40 {
41 sb.append(ch);
42 }
43 }
44
45 return sb.toString();
46 }
47
48 /** *//**
49 * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method}
50 * in the {@link FTPTimestampParser FTPTimestampParser} interface
51 * according to this algorithm:
52 *
53 * If the recentDateFormat member has been defined, try to parse the
54 * supplied string with that. If that parse fails, or if the recentDateFormat
55 * member has not been defined, attempt to parse with the defaultDateFormat
56 * member. If that fails, throw a ParseException.
57 *
58 * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)
59 */
60 public Calendar parseTimestamp(String timestampStr) throws ParseException
61 {
62 timestampStr = formatDate_Zh2En(timestampStr);
63 Calendar now = Calendar.getInstance();
64 now.setTimeZone(this.getServerTimeZone());
65
66 Calendar working = Calendar.getInstance();
67 working.setTimeZone(this.getServerTimeZone());
68 ParsePosition pp = new ParsePosition(0);
69
70 Date parsed = null;
71 if (this.recentDateFormat != null)
72 {
73 parsed = recentDateFormat.parse(timestampStr, pp);
74 }
75 if (parsed != null && pp.getIndex() == timestampStr.length())
76 {
77 working.setTime(parsed);
78 working.set(Calendar.YEAR, now.get(Calendar.YEAR));
79 if (working.after(now))
80 {
81 working.add(Calendar.YEAR, -1);
82 }
83 }
84 else
85 {
86 pp = new ParsePosition(0);
87 parsed = defaultDateFormat.parse(timestampStr, pp);
88 // note, length checks are mandatory for us since
89 // SimpleDateFormat methods will succeed if less than
90 // full string is matched. They will also accept,
91 // despite "leniency" setting, a two-digit number as
92 // a valid year (e.g. 22:04 will parse as 22 A.D.)
93 // so could mistakenly confuse an hour with a year,
94 // if we don't insist on full length parsing.
95 if (parsed != null && pp.getIndex() == timestampStr.length())
96 {
97 working.setTime(parsed);
98 }
99 else
100 {
101 throw new ParseException(
102 "Timestamp could not be parsed with older or recent DateFormat",
103 pp.getIndex());
104 }
105 }
106 return working;
107 }
108 }
109
110
111
(2) 修改org.apache.commons.net.ftp.parser.UnixFTPEntryParser類的parseFTPEntry方法:
1 public FTPFile parseFTPEntry(String entry)
2 {
3  ..
4 if (matches(entry))
5 {
6 String typeStr = group(1);
7 String hardLinkCount = group(15);
8 String usr = group(16);
9 String grp = group(17);
10 String filesize = group(18);
11 String datestr = group(19) + " " + group(20);
12 String name = group(21);
13 String endtoken = group(22);
14
15 try
16 {
17 file.setTimestamp(super.parseTimestamp(datestr));
18 }
19 catch (ParseException e)
20 {
21 /**//* ***mod by hzwei206 將中文時間格式轉(zhuǎn)換 2007-8-15 begin*** */
22 //return null; // this is a parsing failure too.
23 try
24 {
25 FTPTimestampParserImplExZH Zh2En = new FTPTimestampParserImplExZH();
26 file.setTimestamp(Zh2En.parseTimestamp(datestr));
27 }
28 catch (ParseException e1)
29 {
30 return null; // this is a parsing failure too.
31 }
32 /**//* ***mod by hzwei206 將中文時間格式轉(zhuǎn)換 2007-8-15 end*** */
33 }
34
35     ..
36 }
37
|
posted on 2008-08-21 21:30
wodong 閱讀(14940)
評論(1) 編輯 收藏