定義ftp操作接口
import java.io.InputStream; import java.util.List; import org.apache.commons.net.ftp.FTPClient; /** * FTP服務器操作*/ public interface iFtpServU { public FTPClient ftp(String ip, String user, String password); public List<String[]> csv(InputStream in); }
接口實現類
import java.io.IOException; import java.io.InputStream; import java.net.SocketException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPReply; import com.csvreader.CsvReader; /** * FTP服務器操作具體實現*/ public class FtpServUImpl implements iFtpServU { /** 本地字符編碼 */ private static String LOCAL_CHARSET = "GBK"; // FTP協議里面,規定文件名編碼為iso-8859-1 private static String SERVER_CHARSET = "ISO-8859-1"; /** * * <b>登錄ftp 返回ftpClient事件<b> * * @param ip * ftp所在ip * @param user * 登錄名 * @param password * 密碼 */ public FTPClient ftp(String ip, String user, String password) { FTPClient ftpClient = new FTPClient(); try { ftpClient.connect(ip); if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) { if (ftpClient.login(user, password)) { if (FTPReply.isPositiveCompletion(ftpClient.sendCommand( "OPTS UTF8", "ON"))) {// 開啟服務器對UTF-8的支持,如果服務器支持就用UTF-8編碼,否則就使用本地編碼(GBK). LOCAL_CHARSET = "UTF-8"; } ftpClient.setControlEncoding(LOCAL_CHARSET); ftpClient.enterLocalPassiveMode();// 設置被動模式 // ftpClient.setFileType(getTransforModule());// 設置傳輸的模式 } } // ftpClient.login(user, password); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (!ftpClient.isConnected()) { ftpClient = null; } return ftpClient; } /** * <b>將一個IO流解析,轉化數組形式的集合<b> * * @param in * 文件inputStream流 */ public List<String[]> csv(InputStream in) { List<String[]> csvList = new ArrayList<String[]>(); if (null != in) { CsvReader reader = new CsvReader(in, ',', Charset.forName("GBK")); try { // 遍歷每一行,若有#注釋部分,則不處理,若沒有,則加入csvList while (reader.readRecord()) { if (!reader.getValues()[0].contains("#"))// 清除注釋部分 { csvList.add(reader.getValues()); } } } catch (IOException e) { e.printStackTrace(); } reader.close(); } return csvList; } }
業務場景中,調用ftp發生在對賬操作,用戶通過輸入交易筆數、csv文件名,從而判斷ftp服務器上是否存在該文件,如果存在該文件,則首先獲取文件中的內容條數,與輸入的筆數數值比較,一致的話就繼續獲取csv文件內容與系統數據庫中進行比較、對賬,從而完成對賬操作。
import java.io.InputStream; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; //配置文件工具類 import com.online.charge.customer.deployconfig.util.DeployConfigUtil; /** * @ClassName: QueryFtpFilesUtils * @Description: 獲取Ftp上文件列表 * */ public class QueryFtpFilesUtils { /** * 獲取Ftp客戶端 * * @return */ public static FTPClient getFtpClient() { String ip = DeployConfigUtil.getInterfaceDeployConfig().getFtpUrl(); String userName = DeployConfigUtil.getInterfaceDeployConfig() .getFtpUserName(); String userPassword = DeployConfigUtil.getInterfaceDeployConfig() .getFtpPassword(); // 通過配置文件獲取ip,username,password FTPClient ftpClient = getiFtpServU().ftp(ip, userName, userPassword); return ftpClient; } /** * 獲取ftp接口服務 * * @return */ private static iFtpServU getiFtpServU() { return new FtpServUImpl(); } /** * 獲取明細條數 * * @param fileName * csv文件名稱 如:0320180908110523.csv 文件的名稱為 0320180908110523 * @return 明細條數 */ public static Integer getFtpFileCount(String fileName) { Integer count = 0; boolean isContain = Boolean.FALSE; FTPClient ftpClient = getFtpClient(); List<String[]> fileList = null; if (null != ftpClient) { try { FTPFile[] file = ftpClient.listFiles(); if (file == null || file.length <= 0) { return -1; } // 遍歷所有文件,匹配需要查找的文件 for (int i = 0; i < file.length; i++) { // 匹配到則進入 if (file[i].getName().equals(fileName.concat(".csv"))) { isContain = Boolean.TRUE; // 將匹配到的文件流傳入接口,轉化成數組集合 InputStream in = ftpClient.retrieveFileStream(file[i].getName()); fileList = getiFtpServU().csv(in); in.close(); } } if (!isContain) { return -1; } if (CollectionUtils.isNotEmpty(fileList) && fileList.size() > 0) { count = fileList.size(); } } catch (Exception e) { e.printStackTrace(); } } return count; } /** * 獲取FTP文件內容 * * @param fileName * @return */ public static void getFtpFileContent(String fileName, List<String[]> fileContent) { FTPClient ftpClient = getFtpClient(); if (null != ftpClient) { try { FTPFile[] file = ftpClient.listFiles(); // 遍歷所有文件,匹配需要查找的文件 for (int i = 0; i < file.length; i++) { // 匹配到則進入 if (file[i].getName().contains(fileName)) { InputStream in = ftpClient.retrieveFileStream(file[i].getName()); fileContent.addAll(getiFtpServU().csv(in)); in.close(); } } } catch (Exception e) { e.printStackTrace(); } } } }
在測試中,遇到能夠通過 listFiles方法獲取到ftp服務器上的文件列表,但調用 retrieveFileStream方法獲取文件內容失敗(返回不是null),網上找了很多方法,嘗試都失敗,現總結如下:
// 匹配到則進入 if (file[i].getName().equals(fileName.concat(Constant.ACCOUNT_FILE_EXTENDS_NAME))) { isContain = Boolean.TRUE; // 1.設置主動模式 //ftpClient.enterLocalPassiveMode(); // 2.文件類型問題,設置文件類型 //ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 3.編碼問題,設置編碼 //InputStream in = ftpClient.retrieveFileStream(new String(file[i].getName().getBytes("UTF-8"),"ISO-8859-1")); InputStream in = ftpClient.retrieveFileStream(file[i].getName()); // 將匹配到的文件流傳入接口,轉化成數組集合 fileList = getiFtpServU().csv(in); in.close(); // 4.流關閉之后獲取返回狀態 //ftpClient.completePendingCommand(); }
懷疑是 retrieveFileStream方法調用Socket獲取流中間出現的問題,但問題目前無法定位(讀者如果知道原因麻煩不吝賜教!)
為解決該問題,換了個思路:因為ftp上的文件都有數據,且輸入的筆數都大於0,所以當確定ftp上確實有該文件時,首先還是調用 retrieveFileStream方法以流的方式獲取該文件,轉換為數組后判斷,如果長度為0,就說明獲取文件內容失敗,此時換種方法:使用 retrieveFile方法將ftp服務器上的文件下載到本地,通過io流來獲取其內容。實現方法如下:
// 匹配到則進入 if (file[i].getName().equals(fileName.concat(".csv"))) { isContain = Boolean.TRUE; // 將匹配到的文件流傳入接口,轉化成數組集合 InputStream in = ftpClient.retrieveFileStream(file[i].getName()); fileList = getiFtpServU().csv(in); in.close(); if(fileList.size() == 0){ //下載ftp文件到本地 String osFileName = "G:/ftpFiles/"+file[i].getName(); File localFile = new File(osFileName); OutputStream os = new FileOutputStream(localFile); ftpClient.retrieveFile(file[i].getName() , os); os.close(); InputStream ins = new FileInputStream(new File(osFileName)); fileList = getiFtpServU().csv(ins); ins.close(); } }