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:

解決該異常啰嗦了一大堆主要是想告訴一下新手(包括自己),在解決問題時,要懂得跟蹤尋找問題出現的根由,確定原因,再去找解決問題的方案,希望這個異常解決方案能幫到一些人

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM