博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
引用 commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)
阅读量:4052 次
发布时间:2019-05-25

本文共 9387 字,大约阅读时间需要 31 分钟。

引用

 目前开发的这个项目中需要从远程服务器上下载数据,采用了开源的commons.net.ftp包。在实际应用中发现了一个问题,在测试服务器上调用ftpClient.listFiles()方法可以返回包含文件名的数组,而在现网服务器上此方法返回NULL。我被这个问题困扰了好久,下面把我的处理思路陈述如下:

(1)首先发现2个服务器的区别:测试服务器为solaris服务器,而现网服务器为hp服务器,会不会是平台差异所致呢?带着这个问题,下载了common包的源码,通过源码进行调试。

(2)FTPListParseEngine负责处理通过socket来获取远程服务器的信息。大概执行了ls –l

操作,并把结果一行行放入一个linkedlist中。代码如下:

 1commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮private void readStream(InputStream stream, String encoding) throws IOException

 2commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮    commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮{

 3commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        BufferedReader reader;

 4commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        if (encoding == null)

 5commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮{

 6commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮            reader = new BufferedReader(new InputStreamReader(stream));

 7commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        }

 8commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        else

 9commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮{

10commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮            reader = new BufferedReader(new InputStreamReader(stream, encoding));

11commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        }

12commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        

13commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        String line = this.parser.readNextEntry(reader);

14commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮

15commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        while (line != null)

16commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮{

17commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮            this.entries.add(line);

18commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮            line = this.parser.readNextEntry(reader);

19commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        }

20commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        reader.close();

21commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮    }

22commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮

 

(3)这个时候发现问题了,传入line中的字符串中有乱码!正常的应该为:

 

drwxr-xr-x 11 daladmin   daladmin      1024 2004年9月18日 mqm

 

其中时间那部分为乱码。                 

(4)处理:在调用listFiles()之前先调用ftpClient.setControlEncoding("GBK");这样line就能正常显示了,但是listFiles() 返回依然为空!!! 继续.....

(5) 发现继续运行的时候有一个正则表达式匹配不成功,代码如下:

 1commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮 public boolean matches(String s)

 2commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮    commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮{

 3commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        this.result = null;

 4commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        if (_matcher_.matches(s.trim(), this.pattern))

 5commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮{

 6commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮            this.result = _matcher_.getMatch();

 7commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        }

 8commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮        return null != this.result;

 9commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮    }

10commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮

 

s即为(3)中的line,追踪正则表达式,是在具体的子类UnixFTPEntryParser中写死的。如下:

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        */        //问题出在此处,这个匹配只匹配2中形式:        //(1)2008-08-03        //(2)Jan  9或4月 26        //而出错的hp机器下的显示为 8月20日(没有空格分开)        //故无法匹配而报错        //将下面字符串改为:        //((?://d+[-/]//d+[-/]//d+)|(?://S+//s+//S+)|(?://S+))//s+        //便可以成功匹配        + "((?://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*.*)";

 

(6)做上面修改后,能够解析出来,但是接着又会报异常,错误发生在UnixFTPEntryParser类的parseFTPEntry方法中,common.net对中文支持的实在是不够:

try{              file.setTimestamp(super.parseTimestamp(datestr)); }catch (ParseException e){  //注释掉 return null;  // this is a parsing failure too. }commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮

 

这个错误的原因是创建simpleDateFormat类时(详情请见jdkAPI文档)

commons.net包中的FTPClient.listFiles()方法返回null的问题及其解决方案(转)  - 动物凶猛 - 咬死月亮public SimpleDateFormat(String pattern, Locale locale)

 

locale为EN,解决方案是创建一个新类,继承ConfigurableFTPFileEntryParserImpl。其中的属性defaultDateFormat和recentDateFormat 用Locale.CHINA初始化。而我目前的程序用不到取文件的修改时间,所以直接省事将上段代码中的异常吞掉,即注释掉return null 。网上有个解决方案(),是用了另一种方案,粘贴如下:

 

 

 

 

commons-net-1.4.1.jar包中ftp应用的几点问题

一、异常:

         从网站下载了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.
(RegexFTPFileEntryParserImpl.java:75) at org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl.
(ConfigurableFTPFileEntryParserImpl.java:57) at org.apache.commons.net.ftp.parser.UnixFTPEntryParser.
(UnixFTPEntryParser.java:136) at org.apache.commons.net.ftp.parser.UnixFTPEntryParser.
(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引起的,去网站下载该包后放入工程的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类:

/** * FTPTimestampParserImpl的扩展类,使之支持中文环境的时间格式* Date:2007-8-15*/package org.apache.commons.net.ftp.parser;import java.text.ParseException;import java.text.ParsePosition;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;/*** @author hzwei206* FTPTimestampParserImpl的扩展类,使之支持中文环境的时间格式*/public class FTPTimestampParserImplExZH extends FTPTimestampParserImpl    {      private SimpleDateFormat defaultDateFormat = new SimpleDateFormat("mm d hh:mm");      private SimpleDateFormat recentDateFormat = new SimpleDateFormat("yyyy mm d");        /** *//**       * @author hzwei206       * 将中文环境的时间格式进行转换       */      private String formatDate_Zh2En(String timeStrZh)          {          if (timeStrZh == null)              {              return "";          }                  int len = timeStrZh.length();          StringBuffer sb = new StringBuffer(len);          char ch = ' ';          for (int i = 0;i < len;i++)              {              ch = timeStrZh.charAt(i);              if ((ch >= '0' && ch <= '9') || ch == ' ' || ch == ':')                  {                  sb.append(ch);              }          }                  return sb.toString();      }            /** *//**        * Implements the one {@link    FTPTimestampParser#parseTimestamp(String)    method}       * in the {@link    FTPTimestampParser    FTPTimestampParser} interface        * according to this algorithm:       *        * If the recentDateFormat member has been defined, try to parse the        * supplied string with that.    If that parse fails, or if the recentDateFormat       * member has not been defined, attempt to parse with the defaultDateFormat       * member.    If that fails, throw a ParseException.        *        * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String)         */      public Calendar parseTimestamp(String timestampStr) throws ParseException          {          timestampStr = formatDate_Zh2En(timestampStr);          Calendar now = Calendar.getInstance();          now.setTimeZone(this.getServerTimeZone());          Calendar working = Calendar.getInstance();          working.setTimeZone(this.getServerTimeZone());          ParsePosition pp = new ParsePosition(0);          Date parsed = null;          if (this.recentDateFormat != null)              {              parsed = recentDateFormat.parse(timestampStr, pp);          }          if (parsed != null && pp.getIndex() == timestampStr.length())              {              working.setTime(parsed);              working.set(Calendar.YEAR, now.get(Calendar.YEAR));              if (working.after(now))                  {                  working.add(Calendar.YEAR, -1);              }          }          else              {              pp = new ParsePosition(0);              parsed = defaultDateFormat.parse(timestampStr, pp);              // note, length checks are mandatory for us since              // SimpleDateFormat methods will succeed if less than              // full string is matched. They will also accept,              // despite "leniency" setting, a two-digit number as              // a valid year (e.g. 22:04 will parse as 22 A.D.)              // so could mistakenly confuse an hour with a year,              // if we don't insist on full length parsing.              if (parsed != null && pp.getIndex() == timestampStr.length())                  {                  working.setTime(parsed);              }              else                  {                  throw new ParseException(                          "Timestamp could not be parsed with older or recent DateFormat",                          pp.getIndex());              }          }          return working;      }}

 

(2) 修改org.apache.commons.net.ftp.parser.UnixFTPEntryParser类的parseFTPEntry方法:

     

public FTPFile parseFTPEntry(String entry)   { 		..          if (matches(entry))         {             String typeStr = group(1);              String hardLinkCount = group(15);             String usr = group(16);              String grp = group(17);             String filesize = group(18);            String datestr = group(19) + " " + group(20);              String name = group(21);            String endtoken = group(22);              try           {              file.setTimestamp(super.parseTimestamp(datestr));  }       catch (ParseException e)   {            /**//* ***mod by hzwei206 将中文时间格式转换 2007-8-15 begin*** */   //return null; // this is a parsing failure too.           try{             FTPTimestampParserImplExZH Zh2En = new FTPTimestampParserImplExZH();            file.setTimestamp(Zh2En.parseTimestamp(datestr));  }  catch (ParseException e1) {                return null; // this is a parsing failure too. }   /**//* ***mod by hzwei206 将中文时间格式转换 2007-8-15 end*** */  }}

转载地址:http://wctci.baihongyu.com/

你可能感兴趣的文章
python_time模块
查看>>
python_configparser(解析ini)
查看>>
selenium学习资料
查看>>
<转>文档视图指针互获
查看>>
从mysql中 导出/导入表及数据
查看>>
HQL语句大全(转)
查看>>
几个常用的Javascript字符串处理函数 spilt(),join(),substring()和indexof()
查看>>
javascript传参字符串 与引号的嵌套调用
查看>>
swiper插件的的使用
查看>>
layui插件的使用
查看>>
JS牛客网编译环境的使用
查看>>
9、VUE面经
查看>>
关于进制转换的具体实现代码
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
mysql5.6.34 升级到mysql5.7.32
查看>>
dba 常用查询
查看>>
Oracle 异机恢复
查看>>
Oracle 12C DG 搭建(RAC-RAC/RAC-单机)
查看>>
Truncate 表之恢复
查看>>