簡介
- 當前ftp工具類使用java連接,可進行上傳、下載、查詢、刪除(文件或文件夾),需提前在服務器搭建好ftp環境;
- linux搭建ftp環境站點請查看上一篇文章:Linux實例搭建FTP站點;
實例
package com.sixmonth.app.platform.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* -FTP工具類(common-net:ftp)
* -注意:
* 1.-ftpClient.changeWorkingDirectory(ftpPath),切換ftp目錄會產生權限問題,需謹慎使用
* 2.-ftpPath可以寫相對路徑或絕對路徑,一般填寫絕對路徑,以防止工作目錄的切換產生權限問題;如果是相對路徑,一般需要切換回根目錄
* @author sixmonth
* @Date 2019年12月19日
*
*/
public class FtpUtils {
private static Logger logger = LoggerFactory.getLogger(FtpUtils.class);
// ftp服務器地址
public String hostname = "47.111.111.111";
// ftp服務器端口號
public Integer port = 21;
// ftp登錄賬號
public String username = "test";
// ftp登錄密碼
public String password = "123456";
// ftp根目錄(自定義,即vsftpd.conf屬性配置,ep:local_root=/var/ftp/test)
public static String rootPath = "/var/ftp/test/";
/**
* --初始化ftp服務器
*/
public FTPClient initFtpClient() {
FTPClient ftpClient = new FTPClient();
ftpClient.setControlEncoding("utf-8");
try {
logger.info("連接ftp服務器:" + this.hostname + ":" + this.port);
ftpClient.connect(hostname, port); // 連接ftp服務器
ftpClient.login(username, password); // 登錄ftp服務器
int replyCode = ftpClient.getReplyCode(); // 是否成功登錄服務器
if (!FTPReply.isPositiveCompletion(replyCode)) {
logger.info("ftp連接失敗!");
} else {
logger.info("ftp連接成功");
}
} catch (Exception e) {
e.printStackTrace();
}
return ftpClient;
}
/**
* -工作目錄切換回根目錄
*
* @param ftpClient
* @throws IOException
*/
public void changeRootDirectory(FTPClient ftpClient) throws IOException {
ftpClient.changeWorkingDirectory(rootPath);
}
/**
* -上傳文件
* @param ftpClient
* @param ftpPath
* -ftp服務保存地址(歸屬多層文件夾) -注意:
* 1.-當前ftpPath屬於多層文件夾,一般寫法爲:sixmonth/day1;sixmonth/day2
* 2.-也可以寫成服務器的絕對路徑,絕對路徑則屬於單層文件夾,寫法爲:/var/ftp/test
* @param newFileName
* -上傳到ftp的新文件名,有無後綴都行
* @param originfile
* -待上傳文件(文件絕對地址)
* @return
*/
public boolean uploadFile(FTPClient ftpClient, String ftpPath, String newFileName, String originfile) {
InputStream inputStream = null;
try {
logger.info("開始上傳文件");
changeRootDirectory(ftpClient);// 切換根目錄
inputStream = new FileInputStream(new File(originfile));
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setControlEncoding("UTF-8");
CreateDirecroty(ftpClient, ftpPath);
ftpClient.makeDirectory(ftpPath);
// 切換ftp工作目錄,會產生權限問題,需謹慎使用
ftpClient.changeWorkingDirectory(ftpPath);
// 每次數據連接之前,ftp client告訴ftp server開通一個端口來傳輸數據
ftpClient.enterLocalPassiveMode();
// 設置傳輸模式
ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
String temName = newFileName;
if (newFileName.indexOf(".") == -1) {
temName = newFileName + "." + Tool.getPrefix(originfile);
}
// 觀察是否真的上傳成功
boolean storeFlag = ftpClient.storeFile(temName, inputStream);
// boolean storeFlag = ftpClient.storeFile(new
// String(fileName.getBytes("UTF-8"), "ISO-8859-1"), inputStream);
System.err.println("storeFlag==" + storeFlag);
inputStream.close();
logger.info("上傳文件成功");
} catch (Exception e) {
logger.info("上傳文件失敗");
e.printStackTrace();
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
/**
* -上傳文件
* @param ftpClient
* @param ftpPath
* -ftp服務保存地址(歸屬多層文件夾) -注意:
* 1.-當前ftpPath屬於多層文件夾,一般寫法爲:sixmonth/day1;sixmonth/day2
* 2.-也可以寫成服務器的絕對路徑,絕對路徑則屬於單層文件夾,寫法爲:/var/ftp/test
* @param newFileName
* -上傳到ftp的文件名,必須有後綴,ep:test.txt
* @param inputStream
* -輸入文件流
* @return
*/
public boolean uploadFile(FTPClient ftpClient, String ftpPath, String newFileName, InputStream inputStream) {
try {
logger.info("開始上傳文件");
changeRootDirectory(ftpClient);// 切換根目錄
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
CreateDirecroty(ftpClient, ftpPath);
ftpClient.makeDirectory(ftpPath);
// 切換ftp工作目錄,會產生權限問題,需謹慎使用
ftpClient.changeWorkingDirectory(ftpPath);
ftpClient.storeFile(newFileName, inputStream);
inputStream.close();
logger.info("上傳文件成功");
} catch (Exception e) {
logger.error("上傳文件失敗");
e.printStackTrace();
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
/**
* -切換ftp目錄
* @param ftpClient
* @param directory -ftp路徑
* @return
*/
public boolean changeWorkingDirectory(FTPClient ftpClient, String directory) {
boolean flag = true;
try {
flag = ftpClient.changeWorkingDirectory(directory);
if (flag) {
logger.info("進入文件夾" + directory + " 成功!");
} else {
logger.info("進入文件夾" + directory + " 失敗!開始創建文件夾");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
return flag;
}
/**
* -創建多層目錄文件夾,如果有ftp服務器已存在該文件,則不創建,如果無,則創建
* @param ftpClient
* @param remoteDir -ftp文件夾
* @return
* @throws IOException
*/
public boolean CreateDirecroty(FTPClient ftpClient, String remoteDir) throws IOException {
boolean success = true;
String directory = remoteDir + "/";
// 如果遠程目錄不存在,則遞歸創建遠程服務器目錄
if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(ftpClient, new String(directory))) {
int start = 0;
int end = 0;
if (directory.startsWith("/")) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf("/", start);
String path = "";
String paths = "";
while (true) {
String subDirectory = new String(remoteDir.substring(start, end).getBytes("GBK"), "iso-8859-1");
path = path + "/" + subDirectory;
if (!existFile(ftpClient, path)) {
if (makeDirectory(ftpClient, subDirectory)) {
changeWorkingDirectory(ftpClient, subDirectory);
} else {
logger.info("創建目錄[" + subDirectory + "]失敗");
changeWorkingDirectory(ftpClient, subDirectory);
}
} else {
changeWorkingDirectory(ftpClient, subDirectory);
}
paths = paths + "/" + subDirectory;
start = end + 1;
end = directory.indexOf("/", start);
// 檢查所有目錄是否創建完畢
if (end <= start) {
break;
}
}
}
return success;
}
/**
* -判斷ftp服務器文件是否存在
* @param ftpClient
* @param path -文件相對路徑或絕對路徑
* @return
* @throws IOException
*/
public boolean existFile(FTPClient ftpClient, String path) throws IOException {
boolean flag = false;
FTPFile[] ftpFileArr = ftpClient.listFiles(path);
if (ftpFileArr.length > 0) {
flag = true;
}
return flag;
}
/**
* -創建ftp目錄
* @param ftpClient
* @param dir
* @return
*/
public boolean makeDirectory(FTPClient ftpClient, String dir) {
boolean flag = true;
try {
flag = ftpClient.makeDirectory(dir);
if (flag) {
logger.info("創建文件夾" + dir + " 成功!");
} else {
logger.info("創建文件夾" + dir + " 失敗!");
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* - 下載文件
* @param ftpClient
* @param ftpPath
* -FTP服務器文件目錄
* @param filename
* -文件名稱
* @param localpath
* -下載後的文件路徑
* @return
*/
public boolean downloadFile(FTPClient ftpClient, String ftpPath, String filename, String localpath) {
boolean flag = false;
OutputStream os = null;
try {
logger.info("開始下載文件");
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 傳輸的時候以二進制傳輸
ftpClient.enterLocalPassiveMode();
// ftpClient.setRemoteVerificationEnabled(false);
// ftpClient.configure(new
// FTPClientConfig("com.zznode.tnms.ra.c11n.nj.resource.ftp.UnixFTPEntryParser"));
// 由於apache不支持中文語言環境,通過定製類解析中文日期類型
ftpClient.configure(new FTPClientConfig("com.zznode.tnms.ra.c11n.nj.resource.ftp.UnixFTPEntryParser"));
//切換FTP目錄
ftpClient.changeWorkingDirectory(ftpPath);
// 查看有哪些文件夾 以確定切換的ftp路徑正確
String[] a = ftpClient.listNames();
logger.info(a[0]);
FTPFile[] ftpFiles = ftpClient.listFiles();
for (FTPFile file : ftpFiles) {
if (filename.equalsIgnoreCase(file.getName())) {
File localFile = new File(localpath + "/" + file.getName());
os = new FileOutputStream(localFile);
ftpClient.retrieveFile(file.getName(), os);
os.close();
}
}
flag = true;
logger.info("下載文件成功");
} catch (Exception e) {
logger.info("下載文件失敗");
e.printStackTrace();
} finally {
if (null != os) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return flag;
}
/**
* -遞歸遍歷出目錄下面所有文件
* @param ftpClient
* @param ftpPath
* ftp相對路徑或絕對路徑
* @param filePathsArr
* 需要返回的文件路徑集合
* @return
* @throws IOException
*/
public List<String> listFiles(FTPClient ftpClient, String ftpPath, List<String> filePathsArr) throws IOException {
FTPFile[] files = ftpClient.listFiles(ftpPath);
for (FTPFile file : files) {
String path = ftpPath + "/" + file.getName();
if (file.isFile()) {
filePathsArr.add(path);
} else if (file.isDirectory()) {
//過濾當前目錄"."和上一級目錄"..",防止死循環
if (!".".equals(file.getName()) && !"..".equals(file.getName())) {
listFiles(ftpClient, path, filePathsArr);
}
}
}
return filePathsArr;
}
/**
* -刪除單個文件
* @param ftpClient
* @param ftpPath
* -FTP服務器保存目錄
* @param filename
* -要刪除的文件名稱
* @return
*/
public boolean deleteFile(FTPClient ftpClient, String ftpPath, String filename) {
boolean flag = false;
try {
logger.info("開始刪除文件");
// 切換FTP目錄(切換ftp目錄會產生權限問題,則不可與遍歷刪除並用)
// ftpClient.changeWorkingDirectory(ftpPath);
flag = ftpClient.deleteFile(ftpPath + "/" + filename);
if (flag) {
logger.info("刪除文件成功");
} else {
logger.info("刪除文件失敗");
}
} catch (Exception e) {
logger.info("刪除文件失敗");
e.printStackTrace();
}
return flag;
}
/**
* -刪除文件夾內的所有文件以及空文件夾
* -注意:
* 1.-該方法會刪除文件夾內的所有遍歷遞歸文件以及空文件夾
* 2.-該方法需要疊加效果(第一次刪除所有文件,第二次刪除文件夾),第二次可等待下一次執行該方法
* 3.-ftpClient.removeDirectory(ftpPath)的方法對非空文件夾無效
* 4.-ftpClient.changeWorkingDirectory(ftpPath),切換ftp目錄會產生權限問題,需謹慎使用
* @param ftpClient
* @param ftpPath
* -ftp路徑
* @return true 表似成功,false 失敗
*/
public boolean deleteDir(FTPClient ftpClient, String ftpPath) {
boolean flag = false;
try {
flag = iterateDelete(ftpClient, ftpPath);
logger.info("ftp文件刪除成功!");
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
/**
* -遍歷刪除文件以及空文件夾
* @param ftpClient
* @param ftpPath
* @return true 表似成功,false 失敗
* @throws IOException
*/
public boolean iterateDelete(FTPClient ftpClient, String ftpPath) throws IOException {
FTPFile[] files = ftpClient.listFiles(ftpPath);
boolean flag = false;
for (FTPFile f : files) {
String path = ftpPath + "/" + f.getName();
FTPFile[] filesTem = ftpClient.listFiles(path);
if (f.isDirectory() && filesTem.length == 0) {//空文件夾不再進入遞歸
boolean bool = ftpClient.removeDirectory(path);
logger.info("刪除空文件夾:" + bool + " " + path);
} else if (f.isDirectory() && filesTem.length > 0) {//非空文件夾進入遞歸
iterateDelete(ftpClient, path);
} else {//文件直接刪除
deleteFile(ftpClient, ftpPath, f.getName());
}
}
//刪除空文件夾
if (files.length == 0) {
flag = ftpClient.removeDirectory(ftpPath);
} else {
flag = false;
}
return flag;
}
public static void main(String[] args) {
FtpUtils ftp = new FtpUtils();
FTPClient ftpClient = ftp.initFtpClient();
try {
/**
* -上傳文件
*/
// 使用流上傳
// FileInputStream inputStream = new FileInputStream(new File("D:\\sixmonth\\test.txt"));
// ftp.uploadFile(ftpClient,"sixmonth", "test.txt", inputStream);
// 使用文件上傳,同時測試多層目錄
// ftp.uploadFile(ftpClient, "sixmonth/test", "test.txt", "D:\\sixmonth\\test.txt");
// ftp.uploadFile(ftpClient,"sixmonth/test3", "test.txt", "D:\\sixmonth\\test.txt");
/**
* -下載文件
*/
// ftp.downloadFile("sixmonth/test", "test.txt", "D://");
/**
* -刪除文件
*/
// ftp.deleteFile(ftpClient,"sixmonth/test", "test.txt");//刪除單個文件
// ftp.deleteDir(ftpClient,"sixmonth/test");//刪除文件夾
/**
* -遍歷ftp所有文件夾,返回文件路徑集合
*/
List<String> filePathsArr = new ArrayList<String>();
ftp.listFiles(ftpClient, "sixmonth", filePathsArr);
logger.info(filePathsArr.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ftpClient != null) {
try {
ftpClient.logout();
} catch (IOException e1) {
e1.printStackTrace();
}
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
注意事項
- ftpClient.changeWorkingDirectory(ftpPath),切換ftp目錄會產生權限問題,需謹慎使用;
- ftpPath可以寫相對路徑或絕對路徑,一般填寫絕對路徑,以防止工作目錄的切換產生權限問題;如果是相對路徑,多個操作同時進行的話,一般需要切換回根目錄;
- ftpClient.removeDirectory方法只能刪除ftp上的空文件夾,對非空文件夾是無效的的,如果需要刪除文件夾,必須先遍歷刪除所有的文件,然後再刪除空文件夾;
總結
實踐是檢驗認識真理性的唯一標準,自己動手,豐衣足食!