目前開發(fā)的這個(gè)項(xiàng)目中需要從遠(yuǎn)程服務(wù)器上下載數(shù)據(jù),采用了開源的commons.net.ftp包。在實(shí)際應(yīng)用中發(fā)現(xiàn)了一個(gè)問(wèn)題,在測(cè)試服務(wù)器上調(diào)用ftpClient.listFiles()方法可以返回包含文件名的數(shù)組,而在現(xiàn)網(wǎng)服務(wù)器上此方法返回NULL。我被這個(gè)問(wèn)題困擾了好久,下面把我的處理思路陳述如下:
(1)首先發(fā)現(xiàn)2個(gè)服務(wù)器的區(qū)別:測(cè)試服務(wù)器為solaris服務(wù)器,而現(xiàn)網(wǎng)服務(wù)器為hp服務(wù)器,會(huì)不會(huì)是平臺(tái)差異所致呢?帶著這個(gè)問(wèn)題,下載了common包的源碼,通過(guò)源碼進(jìn)行調(diào)試。
(2)FTPListParseEngine負(fù)責(zé)處理通過(guò)socket來(lái)獲取遠(yuǎn)程服務(wù)器的信息。大概執(zhí)行了ls –l
操作,并把結(jié)果一行行放入一個(gè)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)這個(gè)時(shí)候發(fā)現(xiàn)問(wèn)題了,傳入line中的字符串中有亂碼!正常的應(yīng)該為:
drwxr-xr-x 11 daladmin daladmin 1024 2004年9月18日 mqm
|
其中時(shí)間那部分為亂碼。
(4)處理:在調(diào)用listFiles()之前先調(diào)用ftpClient.setControlEncoding("GBK");這樣line就能正常顯示了,但是listFiles() 返回依然為空!!! 繼續(xù).....
(5) 發(fā)現(xiàn)繼續(xù)運(yùn)行的時(shí)候有一個(gè)正則表達(dá)式匹配不成功,代碼如下:
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,追蹤正則表達(dá)式,是在具體的子類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
//問(wèn)題出在此處,這個(gè)匹配只匹配2中形式:
13
//(1)2008-08-03
14
//(2)Jan 9或4月 26
15
//而出錯(cuò)的hp機(jī)器下的顯示為 8月20日(沒有空格分開)
16
//故無(wú)法匹配而報(bào)錯(cuò)
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)做上面修改后,能夠解析出來(lái),但是接著又會(huì)報(bào)異常,錯(cuò)誤發(fā)生在UnixFTPEntryParser類的parseFTPEntry方法中,common.net對(duì)中文支持的實(shí)在是不夠:
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
這個(gè)錯(cuò)誤的原因是創(chuàng)建simpleDateFormat類時(shí)(詳情請(qǐng)見jdkAPI文檔)
public SimpleDateFormat(String pattern, Locale locale)
locale為EN,解決方案是創(chuàng)建一個(gè)新類,繼承ConfigurableFTPFileEntryParserImpl。其中的屬性defaultDateFormat和recentDateFormat 用Locale.CHINA初始化。而我目前的程序用不到取文件的修改時(shí)間,所以直接省事將上段代碼中的異常吞掉,即注釋掉return null 。網(wǎng)上有個(gè)解決方案(http://hi.baidu.com/hzwei206/blog/item/7c901d2debf7e136359bf7cd.html),是用了另一種方案,粘貼如下:
commons-net-1.4.1.jar包中ftp應(yīng)用的幾點(diǎn)問(wèn)題
一、異常:
從http://commons.apache.com網(wǎng)站下載了commons-net-1.4.1包后添加到自己的工程中,調(diào)用FtpClient類的listFiles(String pathName)方法時(shí),拋如下異常:
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中,重新編譯運(yùn)行,OK!
二、調(diào)用FtpClient類的listFiles(String pathName)方法失效的問(wèn)題:
一般是由于ftp服務(wù)器(主要是小型機(jī))的操作系統(tǒng)不同語(yǔ)言環(huán)境的時(shí)間格式造成的,在中文環(huán)境下,文件或文件夾的時(shí)間格式為"m月d日 hh:mm"或"yyyy年m月 d",而E文環(huán)境下時(shí)間格式為"MMM d yyyy"或"MMM d HH:mm",于是,在中文環(huán)境下,ftp包中的FTPTimestampParserImpl類將時(shí)間字符串Date化時(shí)拋異常,因?yàn)閏ommons-net-1.4.1包不支持中文。
解決辦法(兩種辦法):
1. 將ftp服務(wù)器操作系統(tǒng)語(yǔ)言環(huán)境設(shè)為英文;
2. 修改ftp包的代碼:將FTPTimestampParserImpl類進(jìn)行擴(kuò)展,使之支持中文
下面針對(duì)第2種解決辦法來(lái)實(shí)現(xiàn):
(1) 新建類FTPTimestampParserImplExZH類:
1 /** *//**
2 * FTPTimestampParserImpl的擴(kuò)展類,使之支持中文環(huán)境的時(shí)間格式
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的擴(kuò)展類,使之支持中文環(huán)境的時(shí)間格式
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)境的時(shí)間格式進(jì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 將中文時(shí)間格式轉(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 將中文時(shí)間格式轉(zhuǎn)換 2007-8-15 end*** */
33 }
34
35     ..
36 }
37
|
posted on 2008-08-21 21:30
wodong 閱讀(14940)
評(píng)論(1) 編輯 收藏