首先我們需要查看是否已經安裝vsftpd,輸入命令 :vsftpd -v。如果出現以下信息,那么就說明已經安裝vsftpd

如果沒有安裝,那么輸入命令 : yum install vsftpd -y 進行安裝,出現complete說明安裝成功。
現在去我們先創建一下用戶 , 命令:useradd -d /home/ftpuser ftpuser -d是為用戶ftpuser 指定主目錄,默認是/home下對應用戶名的一個文件夾。
接着設置用戶密碼 : passwd ftpuser .輸入兩次以后顯示successfully就說明設置好了。
接着我們需要查看一下本機外網訪問權限,這個一定要開。不然會執行失敗:輸入 getsebool -a |grep ftp

我們發現這兩個狀態是關着的,我們要把他開起來,同事也要關閉防火牆


這樣子就開起來了。接下去我們需要配置一下vsftpd 的配置文件 進入配置文件目錄

其實默認的配置文件就可以滿足基本的功能,如果我們需要關閉匿名操作,那么我們把anonymous_enable設置為NO就可以了。
vsftpd的默認端口為21,在不太了解vsftpd的情況下,切記不要去改他。一定不要去改,一定不要!!
接下去就是JAVA代碼了
上傳功能的代碼:上傳過程可能遇到進入文件夾失敗,這里推薦大家用絕對路徑!!!
/**
* 初始化ftp服務器
*
* @throws IOException
* @throws SocketException
*/
public void initFtpClient() throws SocketException, IOException {
ftpClient = new FTPClient();
ftpClient.setControlEncoding("utf-8");
LOGGER.info("connecting...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("connect failed...ftp服務器:" + this.hostname + ":" + this.port);
}
LOGGER.info("connect successfu...ftp服務器:" + this.hostname + ":" + this.port);
}
/**
* 上傳文件
*
* @param pathname
* ftp服務保存地址
* @param fileName
* 上傳到ftp的文件名
* @param inputStream
* 輸入文件流
* @return
* @throws IOException
*/
public boolean uploadFile(String pathname, String fileName, InputStream inputStream) throws IOException {
boolean flag = false;
initFtpClient();
try {
LOGGER.info("開始上傳文件");
ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
// CreateDirecroty(pathname);
boolean changeWorkingDirectory = ftpClient.changeWorkingDirectory(pathname);
if(changeWorkingDirectory) {
LOGGER.info("進入文件"+pathname+"夾成功.");
}else {
LOGGER.info("進入文件"+pathname+"夾失敗.開始創建文件夾");
boolean makeDirectory = ftpClient.makeDirectory(pathname);
if(makeDirectory) {
LOGGER.info("創建文件夾"+pathname+"成功");
boolean changeWorkingDirectory2 = ftpClient.changeWorkingDirectory(pathname);
if(changeWorkingDirectory2) {
LOGGER.info("進入文件"+pathname+"夾成功.");
}
}else {
LOGGER.info("創建文件夾"+pathname+"失敗");
}
}
ftpClient.storeFile(fileName, inputStream);
inputStream.close();
ftpClient.logout();
flag = true;
if (flag) {
LOGGER.info("上傳文件成功");
}
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
接下去就是上傳的功能了,我這里約到一個很奇怪的現象,就是通過ftpclientd獲取文件列表的時候有隨機性,有時候可以獲取到,有時候卻是空的,就是ftpclient.listFiles()方法。網上的說法是添加方法ftpClient.enterLocalPassiveMode();,可是我這里添加了這個方法就連接超時。也有人說是防火牆一系列問題。那么首先是vsftpd的主動被動問題。vsftpd的配置文件是默認啟用的主動模式。要改成被動模式的話,這邊需要修改配置文件,我用的主動模式 。輸入命令 vim /etc/vsftpd/vsftpd.conf ,修改之前記得先備份一份配置文件。 然后:

這么改,啟動被動模式,關閉主動模式,隨后重啟vsftpd.我試過僅僅這樣子改是不行的,我們需要去查看一下SELinux防火牆的狀態:

我這里是寬容模式。SELinux有3種狀態,Permissive (寬容模式), Disabled , Enforcing(強制)在配置文件/etc/selinux/config中定義,要永久關閉SELinux防火牆需要將SELINUX=Permissive改為SELINUX=disabled,這里一定要重啟系統。如果是啟用就將disabled改成其他的,也需要重啟。我這邊是強制改成寬容,只需要輸入命令setenforce 1 就可以了,不需要重啟計算機。修改完這里,我們還要設置防火牆FireWall防火牆。
firewall-cmd --permanent --zone=public --add-port=10090-10100/tcp
firewall-cmd --reload
顯示success就可以了。。。
接下去要增加兩個模塊。。不然被動模式會連接失敗,編輯 /etc/sysconfig/iptables-config 文件
IPTABLES_MODULES="ip_conntrack_ftp"
IPTABLES_MODULES="ip_nat_ftp"
添加完這兩行 保存設置 firewall-cmd --reload 。
這里的配置有亂,我后來把防火牆也禁用了。SELinux也禁用了。

可能根本不需要這些配置,大家在實操的時候可以先把防火牆跟SELinux都先禁用。看看實際運行情況再考慮要不要做這些配置,我這里是遇到獲取不到文件。所以百度了很多文章,做了很多配置,畢竟不熟悉。
接下去就是下載 跟 刪除的代碼了:
/**
* * 下載文件 *
*
* @param pathname
* FTP服務器文件目錄 *
* @param filename
* 文件名稱 *
* @param localpath
* 下載后的文件路徑 *
* @return
* @throws IOException
*/
public byte[] downloadFile(String pathname, String filename, String localpath) throws IOException {
boolean flag = false;
OutputStream os = null;
byte[] buffer = null;
initFtpClient();
try {
LOGGER.info("開始下載文件");
// ftpClient.enterLocalPassiveMode();
// FTPClientConfig conf = new FTPClientConfig( FTPClientConfig.SYST_UNIX);
// ftpClient.configure(conf);
// 切換FTP目錄
boolean changeWorkingDirectory = ftpClient.changeWorkingDirectory(pathname);
if(!changeWorkingDirectory) {
throw new FTPTransferException(FTPError.changeDirectoryError) ;
}else {
LOGGER.info("進入目"+pathname+"錄成功。");
}
FTPFile[] ftpFiles = ftpClient.listFiles(pathname, new FTPFileFilter() {
@Override
public boolean accept(FTPFile file) {
if (file.getName().equals(filename)) {
return true;
}
return false;
}
});
for (int i=0;i<ftpFiles.length && flag ==false;i++) {
FTPFile file = ftpFiles[i];
if (filename.equalsIgnoreCase(file.getName())) {
flag = true;
setFileSize(file.getSize());
InputStream fis = ftpClient.retrieveFileStream(file.getName());
buffer = new byte[fis.available()];
int read = fis.read(buffer);
fis.close();
LOGGER.info("下載文件成功");
}
}
ftpClient.logout();
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != os) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return buffer;
}
/**
* * 刪除文件 *
*
* @param pathname
* FTP服務器保存目錄 *
* @param filename
* 要刪除的文件名稱 *
* @return
* @throws IOException
*/
public boolean deleteFile(String pathname, String filename) throws IOException {
boolean flag = false;
try {
LOGGER.info("開始刪除文件");
initFtpClient();
// 切換FTP目錄
boolean changeWorkingDirectory = ftpClient.changeWorkingDirectory(pathname);
if(!changeWorkingDirectory) {
throw new FTPTransferException(FTPError.changeDirectoryError) ;
}else {
LOGGER.info("進入目"+pathname+"錄成功。");
}
FTPFile[] ftpFiles = ftpClient.listFiles(pathname, new FTPFileFilter() {
@Override
public boolean accept(FTPFile file) {
if (file.getName().equals(filename)) {
return true;
}
return false;
}
});
if (ftpFiles == null || ftpFiles.length == 0) {
LOGGER.info("刪除文件失敗,文件不存在");
throw new FTPTransferException(FTPError.fileNotFound) ;
} else {
ftpClient.dele(filename);
ftpClient.logout();
flag = true;
}
if(flag) {
LOGGER.info("刪除文件成功");
}
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return flag;
}
這樣下來基本功能就差不多都可以實現了。。經過驗證,獲取不到列表的我,應該不是主動被動的關系,到最后我還是改成了主動,最終確定是SELinux的鍋,一定把他的強制模式關閉。在調試前,一定要看看SELinux的狀態,跟防火牆的狀態。都給他關了 排除干擾,一般來說程序就沒什么問題了
