隨筆 - 1  文章 - 1  trackbacks - 0
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(2)

          隨筆檔案

          搜索

          •  

          最新評論

           

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

          1)首先發現2個服務器的區別:測試服務器為solaris服務器,而現網服務器為hp服務器,會不會是平臺差異所致呢?帶著這個問題,下載了common包的源碼,通過源碼進行調試。

          2FTPListParseEngine負責處理通過socket來獲取遠程服務器的信息。大概執行了ls –l

          操作,并把結果一行行放入一個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)這個時候發現問題了,傳入line中的字符串中有亂碼!正常的應該為:

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


          其中時間那部分為亂碼。                 

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

          (5) 發現繼續運行的時候有一個正則表達式匹配不成功,代碼如下:

           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中寫死的。如下:

           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機器下的顯示為 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)做上面修改后,能夠解析出來,但是接著又會報異常,錯誤發生在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

           

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

          public SimpleDateFormat(String pattern, Locale locale)

           

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

           

          commons-net-1.4.1.jar包中ftp應用的幾點問題


          一、異常:
                   從http://commons.apache.com網站下載了commons-net-1.4.1包后添加到自己的工程中,調用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網站下載該包后放入工程的lib下,并加載到classpath中,重新編譯運行,OK!

          二、調用FtpClient類的listFiles(String pathName)方法失效的問題:
                一般是由于ftp服務器(主要是小型機)的操作系統不同語言環境的時間格式造成的,在中文環境下,文件或文件夾的時間格式為"m月d日 hh:mm"或"yyyy年m月 d",而E文環境下時間格式為"MMM d yyyy"或"MMM d HH:mm",于是,在中文環境下,ftp包中的FTPTimestampParserImpl類將時間字符串Date化時拋異常,因為commons-net-1.4.1包不支持中文。
          解決辦法(兩種辦法):
                  1. 將ftp服務器操作系統語言環境設為英文;
                   2. 修改ftp包的代碼:將FTPTimestampParserImpl類進行擴展,使之支持中文
          下面針對第2種解決辦法來實現:
          (1)    新建類FTPTimestampParserImplExZH類:

            1/**
            2* FTPTimestampParserImpl的擴展類,使之支持中文環境的時間格式
            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的擴展類,使之支持中文環境的時間格式
           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       * 將中文環境的時間格式進行轉換
           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 將中文時間格式轉換 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 將中文時間格式轉換 2007-8-15 end*** */
          33              }

          34
          35              ..
          36      }

          37

          posted on 2008-08-21 21:30 wodong 閱讀(14945) 評論(1)  編輯  收藏

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

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


          網站導航:
           
          主站蜘蛛池模板: 丹阳市| 志丹县| 宁强县| 宁海县| 松滋市| 舒城县| 永仁县| 盖州市| 罗平县| 上杭县| 清远市| 巴塘县| 新源县| 融水| 原平市| 兴城市| 栾城县| 海兴县| 政和县| 潜山县| 九江市| 濮阳县| 鄂尔多斯市| 全州县| 秭归县| 通州市| 江城| 荔浦县| 长春市| 景泰县| 田阳县| 和龙市| 壤塘县| 尚志市| 新龙县| 库车县| 长泰县| 林周县| 马公市| 凉城县| 宜城市|