隨筆 - 1  文章 - 1  trackbacks - 0
          <2012年6月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          常用鏈接

          留言簿(2)

          隨筆檔案

          搜索

          •  

          最新評論

           

              目前開發(fā)的這個項(xiàng)目中需要從遠(yuǎn)程服務(wù)器上下載數(shù)據(jù),采用了開源的commons.net.ftp包。在實(shí)際應(yīng)用中發(fā)現(xiàn)了一個問題,在測試服務(wù)器上調(diào)用ftpClient.listFiles()方法可以返回包含文件名的數(shù)組,而在現(xiàn)網(wǎng)服務(wù)器上此方法返回NULL。我被這個問題困擾了好久,下面把我的處理思路陳述如下:

          1)首先發(fā)現(xiàn)2個服務(wù)器的區(qū)別:測試服務(wù)器為solaris服務(wù)器,而現(xiàn)網(wǎng)服務(wù)器為hp服務(wù)器,會不會是平臺差異所致呢?帶著這個問題,下載了common包的源碼,通過源碼進(jìn)行調(diào)試。

          2FTPListParseEngine負(fù)責(zé)處理通過socket來獲取遠(yuǎn)程服務(wù)器的信息。大概執(zhí)行了ls –l

          操作,并把結(jié)果一行行放入一個linkedlist中。代碼如下:

           1private 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中的字符串中有亂碼!正常的應(yīng)該為:

          drwxr-xr-x 11 daladmin   daladmin      1024 2004年918 mqm


          其中時間那部分為亂碼。                 

          4)處理:在調(diào)用listFiles()之前先調(diào)用ftpClient.setControlEncoding("GBK");這樣line就能正常顯示了,但是listFiles() 返回依然為空!!! 繼續(xù).....

          (5) 發(fā)現(xiàn)繼續(xù)運(yùn)行的時候有一個正則表達(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中寫死的。如下:

           1private 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機(jī)器下的顯示為 8月20日(沒有空格分開)
          16        //故無法匹配而報(bào)錯
          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)做上面修改后,能夠解析出來,但是接著又會報(bào)異常,錯誤發(fā)生在UnixFTPEntryParser類的parseFTPEntry方法中,common.net對中文支持的實(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

           

          這個錯誤的原因是創(chuàng)建simpleDateFormat類時(詳情請見jdkAPI文檔)

          public SimpleDateFormat(String pattern, Locale locale)

           

          localeEN,解決方案是創(chuàng)建一個新類,繼承ConfigurableFTPFileEntryParserImpl。其中的屬性defaultDateFormatrecentDateFormat Locale.CHINA初始化。而我目前的程序用不到取文件的修改時間,所以直接省事將上段代碼中的異常吞掉,即注釋掉return null 。網(wǎng)上有個解決方案(http://hi.baidu.com/hzwei206/blog/item/7c901d2debf7e136359bf7cd.html,是用了另一種方案,粘貼如下:
           

           

          commons-net-1.4.1.jar包中ftp應(yīng)用的幾點(diǎn)問題


          一、異常:
                   從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中,重新編譯運(yùn)行,OK!

          二、調(diào)用FtpClient類的listFiles(String pathName)方法失效的問題:
                一般是由于ftp服務(wù)器(主要是小型機(jī))的操作系統(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化時拋異常,因?yàn)閏ommons-net-1.4.1包不支持中文。
          解決辦法(兩種辦法):
                  1. 將ftp服務(wù)器操作系統(tǒng)語言環(huán)境設(shè)為英文;
                   2. 修改ftp包的代碼:將FTPTimestampParserImpl類進(jìn)行擴(kuò)展,使之支持中文
          下面針對第2種解決辦法來實(shí)現(xiàn):
          (1)    新建類FTPTimestampParserImplExZH類:

            1/**
            2* FTPTimestampParserImpl的擴(kuò)展類,使之支持中文環(huán)境的時間格式
            3* Date:2007-8-15
            4*/

            5package org.apache.commons.net.ftp.parser;
            6
            7import java.text.ParseException;
            8import java.text.ParsePosition;
            9import java.text.SimpleDateFormat;
           10import java.util.Calendar;
           11import java.util.Date;
           12
           13/**
           14@author hzwei206
           15* FTPTimestampParserImpl的擴(kuò)展類,使之支持中文環(huán)境的時間格式
           16*/

           17public 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)境的時間格式進(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 將中文時間格式轉(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 閱讀(14947) 評論(1)  編輯  收藏

          FeedBack:
          # 150 Here comes the directory listing[未登錄] 2012-06-08 15:45 cnwong
          還有可能存在的現(xiàn)象:卡死在“150 Here comes the directory listing.”一行不再往下執(zhí)行,說明FTPClient.class -->socket=server.accept()被鎖死,原因是FTPClient.class -->getActivePort()返回了0,而你linux客戶端又恰巧不開放該端口。將getActivePort()的返回值設(shè)置成linux客戶端開放的且未被占用的端口值即可(一般是80XX)。  回復(fù)  更多評論
            

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 安阳县| 翼城县| 平山县| 横峰县| 铅山县| 黔西县| 秦皇岛市| 浦县| 威宁| 通化县| 应用必备| 道真| 玛纳斯县| 上高县| 鹤岗市| 五台县| 科技| 白河县| 灌阳县| 项城市| 东宁县| 疏勒县| 林芝县| 黔西县| 抚松县| 贺州市| 徐水县| 乐至县| 哈尔滨市| 贵港市| 五峰| 桦川县| 台山市| 清涧县| 临漳县| 黑河市| 南通市| 宜章县| 清远市| 临高县| 巴东县|