首先我們需要查看是否已經安裝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的狀態,跟防火牆的狀態。都給他關了 排除干擾,一般來說程序就沒什么問題了