`

FTPClient 的listFiles()方法问题

阅读更多

目前开发的这个项目中需要从远程服务器上下载数据,采用了开源的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
分享到:
评论

相关推荐

    解决ftpClient.listFiles()结果为空的问题

    CentOS某些版本中,JAVA代码执行ftpClient.listFiles()有时结果为空。本项目解决了这个问题。具体代码思路可以参考博客:http://blog.csdn.net/yingprince/article/details/51275956

    FTPClient.listFiles()获取文件为空的问题.

    ftpClient.changeWorkingDirectory(path); ftpClient.enterLocalPassiveMode(); //由于apache不支持中文语言环境,通过定制类解析中文日期类型 ... FTPFile[] files = ftpClient.listFiles();

    ftpClient.listFiles()返回值为空的解决方案(上一个传错了)

    解决了某些系统中,ftpClient.listFiles()返回值总是为空的问题。 使用方法如下: ftpClient.changeWorkingDirectory(path); ftpClient.enterLocalPassiveMode(); ftpClient.configure(new FTPClientConfig(...

    FTPClient.

    FTPFile[] files = ftpClient.listFiles(); for (FTPFile file : files) { System.out.println(file.getName()); } ftpClient.makeDirectory("newDirectory"); boolean deleteSuccess = ftpClient.deleteFile(...

    commons-ftp中ftpClient类的API

    4. **目录操作**:可以使用`changeWorkingDirectory()`, `listNames()`, `listFiles()`等方法来切换工作目录、获取目录列表。 ```java ftp.changeWorkingDirectory("/remote/directory"); String[] dirs = ftp.list...

    Apache的FTPClient.jar

    6. 获取文件信息:`FTPClient.listFiles(String dir)`返回服务器上指定目录下的文件和目录列表。 7. 保持会话状态:`FTPClient.setFileType(FTP.BINARY_FILE_TYPE)`设置文件传输类型为二进制,适用于大多数文件。...

    apache java ftpclient

    使用`FTPClient.listNames()`或`FTPClient.listFiles()`方法可以获取远程目录中的文件和子目录列表。 6. **删除文件和目录** 删除文件使用`FTPClient.deleteFile()`方法,而删除目录则需先改变到该目录,然后使用...

    使用commons.net FTP 和sun.net.ftp.FtpClient 多种方式上传下载(包括批量)删除功能(一)

    `FTPClient.listFiles()`返回服务器目录下的文件和子目录列表。 7. **删除文件**:使用`FTPClient.deleteFile(String filename)`删除指定的远程文件。 8. **断开连接**:完成所有操作后,确保使用`FTPClient....

    java ftpClient 工具包代码

    FTPFile[] files = ftpClient.listFiles(remoteDir); for (FTPFile file : files) { if (file.isDirectory()) { File localSubDir = new File(localDir + File.separator + file.getName()); localSubDir.mkdir...

    JAVA FTPClient文件操作

    FTPFile[] files = ftpClient.listFiles("remote/directory"); for (FTPFile file : files) { System.out.println(file.getName()); } ``` ### 错误处理 在进行FTP操作时,应捕获并处理可能的异常,如`...

    java编写的ftpclient,用于向ftp发送、获取文件

    6. **目录操作**:`listFiles`返回服务器上的目录内容,`mkdir`用于创建新目录,`deleteFile`删除文件或目录(需谨慎使用)。 7. **错误处理**:使用`boolean`类型的FTPClient方法(如`login`,`storeFile`等)会...

    JavaFtpClient.rar_JAVAFTP_ftpclient

    FTPFile[] files = ftpClient.listFiles(); for (FTPFile file : files) { System.out.println(file.getName()); } ``` 完成所有操作后,别忘了关闭连接: ```java ftpClient.logout(); ftpClient.disconnect(); ...

    FTPClient Class

    5. **目录操作**:FTPClient 还支持列出服务器目录(`listFiles()`)、切换当前目录(`changeWorkingDirectory()`)和创建新目录(`makeDirectory()`)等功能。 6. **状态查询和控制**:FTP 客户端可以检查连接状态...

    FTPCLIENT_commons-net-1.4.1_jakarta-oro-2.0.8

    2. **目录操作**:`FTPClient`提供了`changeWorkingDirectory`方法来切换当前工作目录,`listDirectories`和`listFiles`方法用于列出目录及其内容。 3. **文件上传**:`storeFile`方法用于上传文件到服务器,需要...

    android ftpclient实例

    FTPFile[] files = ftpClient.listFiles(); for (FTPFile file : files) { Log.d("FTP", file.getName()); } ftpClient.changeWorkingDirectory("/new_directory"); ``` 7. **断开连接**: 完成操作后,别忘...

    Apache Commons Net » 3.3 org.apache.commons.net.ftp.FTPClient

    4. **目录操作**:`changeWorkingDirectory(directory)`允许切换当前工作目录,`printWorkingDirectory()`返回当前目录,`listDirectories()`和`listFiles()`则分别用于获取目录和文件列表。 5. **文件和权限管理**...

    Java常用FTP文件操作说明Apache.FTPClient,ftp4j,jftp

    本文将详细讲解三种常用的FTP客户端库——Apache.FTPClient、ftp4j及jftp,并以Apache.FTPClient为例,深入探讨其使用方法。 #### 一、Apache.FTPClient简介 Apache Commons Net是Apache项目下的一个开源工具包,...

    commons-ftp中ftpClient类的API.pdf

    5. 文件操作:文档提到了使用FTPClient进行文件操作的方法,包括登录服务器后使用listFiles()列出服务器上的文件列表。还可以使用FTPFileEntryParser和FTPListParseEngine来解析和处理文件列表。 6. FTPClient类的...

    利用ftpclient下载文件,解决用window.open打开ftp地址中有中文,不能下载的问题

    FTPFile[] fs = ftp.listFiles(); for (FTPFile ff : fs) { if (ff.getName().equals(fileName)) { File localFile = new File(localPath + File.separator + ff.getName()); // 创建输出流 OutputStream is...

Global site tag (gtag.js) - Google Analytics