it.sauronsoftware.ftp4j.FTPListParseException异常及解决方案

最近在做一个FTP数据采集功能,在使用ftp4j组件(官网下载地址:http://www.sauronsoftware.it/projects/ftp4j/download.php?PHPSESSID=7ugub8n90o29g1u64muqlss2c3)做FTP目录文件扫描时,遇到了一个纠结的问题。

模拟问题场景:

FTP服务器目录如下:

 

扫描FTP目录文件代码片段如下:

FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect("192.168.10.145", 21);
            System.out.println("连接成功");
            ftpClient.login("monkey1992", "123456");
            System.out.println("登录成功");
            ftpClient.changeDirectory("data");
            String[]  files = ftpClient.listNames();
            System.out.println(Arrays.toString(files));
            ftpClient.disconnect(true);
        } catch (Exception e) {
            e.printStackTrace();
        }

 

使用listNames()方法,可以正常扫描出指定目录文件的文件名,输出结果如下:

连接成功
登录成功
[data.txt, data.xls, test.doc]

ftp4j组件提供的扫描目录文件的方法除了listNames()外,还提供了list(), list(String fileSpec)方法,返回值都是FTPFile[]

现在想使用的是使用list()方法扫描FTP服务器中指定的目录,然而今天所要解决的问题出现了,调用list()方法抛出了异常,修改代码如下:

FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect("192.168.0.132", 21);
            System.out.println("连接成功");
            ftpClient.login("monkey1992", "106");
            System.out.println("登录成功");
            ftpClient.changeDirectory("data");
            FTPFile[]  files = ftpClient.list();   //listNames改成list()
            System.out.println(Arrays.toString(files));
            ftpClient.disconnect(true);
        } catch (Exception e) {
            e.printStackTrace();
        }

结果如下:

连接成功
登录成功
it.sauronsoftware.ftp4j.FTPListParseException
    at it.sauronsoftware.ftp4j.FTPClient.list(FTPClient.java:2131)
    at it.sauronsoftware.ftp4j.FTPClient.list(FTPClient.java:2182)
    at accel.component.datacollect.ftp.FtpMainTest.test(FtpMainTest.java:51)
    at accel.component.datacollect.ftp.FtpMainTest.main(FtpMainTest.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

 

通过断点调试,跟踪该异常是在it.sauronsoftware.ftp4j.listparsers.DOSListParser列表解析类中,在对列表文件的更新时间进行解析时出现了异常

DOSListParser类的源代码如下:

package it.sauronsoftware.ftp4j.listparsers;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPListParseException;
import it.sauronsoftware.ftp4j.FTPListParser;

/**
 * This parser can handle the MSDOS-style LIST responses.
 * 
 * @author Carlo Pelliccia
 */
public class DOSListParser implements FTPListParser {

    private static final Pattern PATTERN = Pattern
            .compile("^(\\d{2})-(\\d{2})-(\\d{2})\\s+(\\d{2}):(\\d{2})(AM|PM)\\s+"
                    + "(<DIR>|\\d+)\\s+([^\\\\/*?\"<>|]+)$");

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat(
            "MM/dd/yy hh:mm a");

    public FTPFile[] parse(String[] lines) throws FTPListParseException {
        int size = lines.length;
        FTPFile[] ret = new FTPFile[size];
        for (int i = 0; i < size; i++) {
            Matcher m = PATTERN.matcher(lines[i]);
            if (m.matches()) {
                String month = m.group(1);
                String day = m.group(2);
                String year = m.group(3);
                String hour = m.group(4);
                String minute = m.group(5);
                String ampm = m.group(6);
                String dirOrSize = m.group(7);
                String name = m.group(8);
                ret[i] = new FTPFile();
                ret[i].setName(name);
                if (dirOrSize.equalsIgnoreCase("<DIR>")) {
                    ret[i].setType(FTPFile.TYPE_DIRECTORY);
                    ret[i].setSize(0);
                } else {
                    long fileSize;
                    try {
                        fileSize = Long.parseLong(dirOrSize);
                    } catch (Throwable t) {
                        throw new FTPListParseException();
                    }
                    ret[i].setType(FTPFile.TYPE_FILE);
                    ret[i].setSize(fileSize);
                }
                String mdString = month + "/" + day + "/" + year + " " + hour
                        + ":" + minute + " " + ampm;
                Date md;
                try {
                    md = DATE_FORMAT.parse(mdString); //解析字符串转为日期类型时出异常
                } catch (ParseException e) {
                    throw new FTPListParseException();
                }
                ret[i].setModifiedDate(md);
            } else {
                throw new FTPListParseException();
            }
        }
        return ret;
    }

}

异常信息如下:

 

 

所以it.sauronsoftware.ftp4j.FTPListParseException异常出现的原因是因为SimpleDateFormat的parse()解析方法上

 

为什么字符串解析成日期会抛异常,下面做个小实验

SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy hh:mm a");
        try {
            Date date = dateFormat.parse("03/05/13 12:00 AM");
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }

输出结果:

java.text.ParseException: Unparseable date: "03/05/13 12:00 AM"
    at java.text.DateFormat.parse(DateFormat.java:337)
    at accel.component.datacollect.ftp.FtpMainTest.main(FtpMainTest.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)


异常原因分析:

其实之所以出现日期解析异常,关键是在系统时间的语言环境设置上。由于我的机器系统时间采用的我们国家的,AM或PM这种日期描述我们是没有的。所以自然解析不了,下面是我机器的时间语言环境设置:

 

现在,我把机器语言改成美国的,然后再运行之前的日期解析实验程序

 

程序输出结果:

Tue Mar 05 00:00:00 CST 2013


现在你可能提出疑问,那解决该日期解析异常一定要改系统的时间语言环境?

答案肯定是否定的。在SimpleDateFormat中提供了下面这种构造方法

SimpleDateFormat(String pattern, Locale locale)   ---> 用给定的模式和给定语言环境的默认日期格式符号构造 SimpleDateFormat

所以上述的实验代码,可以修改成下面这种方式,就无需修改系统的时间语言环境,代码如下:

SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy hh:mm a", Locale.ENGLISH);
        try {
            Date date = dateFormat.parse("03/05/13 12:00 AM");
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }

所以解决FTPListParseException异常,可以修改it.sauronsoftware.ftp4j.listparsers.DOSListParser类中的SimpleDateFormat的解析模式
现在你可能会再次发出疑问,解决该异常要修改ftp4j源代码太不靠谱了。

下面还有一种解决该异常的方法,就是修改FTP服务器中的目录列表样式,默认是MS-DOS(M),现在改成UNIX(U)如下图:

 

修改好后,调用FTPClient的list()方法就可以正常返回FTPFile数组了

 

总结:

解决it.sauronsoftware.ftp4j.FTPListParseException异常的方案主要有以下3个:

(1) 修改系统时间语言环境  (不靠谱,不推荐使用)

(2)修改it.sauronsoftware.ftp4j.listparsers.DOSListParser类的SimpleDateFormat的解析模式 (直接使用官网Jar时不推荐使用,需要修改代码--编译--再打包,但如果你是直接提取ftp4j组件的源代码,自己拿来做二次封装,简单的说就是直接拿该组件的源码在导到项目中使用,这种情况下就可以使用这种方式)

(3)修改FTP服务器中的目录列表样式(推荐使用)

PS:

解决该异常啰嗦了一大堆主要是想告诉一下新手(包括自己),在解决问题时,要懂得跟踪寻找问题出现的根由,确定原因,再去找解决问题的方案,希望这个异常解决方案能帮到一些人

 

 

 

 

 

 

 

posted @ 2013-07-06 14:32  dirainy  阅读(5938)  评论(1编辑  收藏  举报