本地Windows開發環境下,FTP傳輸文件正常,但將服務部署到Linux環境時,會出現無響應的狀態。
1 在FTP服務中,涉及到客戶端和服務器端的連接,連接就會涉及到端口的打開問題;
2 而端口的打開中,又涉及到主動模式和被動模式。主動模式:客戶端開放端口給服務端用;被動模式:服務端開放端口給客戶端用。
由於很多客戶端在防火牆內,開放端口給服務器端用比較困難。所以用被動模式的時候比較多。
所以在storeFile(),listFiles()時之前需要調用 ftpClient.enterLocalPassiveMode();
這個方法的意思就是每次數據連接之前,ftpClient告訴ftp server開通一個端口來傳輸數據。
FTP傳輸文件
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.MalformedURLException;
@Slf4j
public class FTPUtil {
//ftp服務器地址
private static String HOST_NAME = PropertiesUtil.getProperty("ftp.server.ip");
//ftp服務器端口號默認爲21
private static Integer PORT = Integer.valueOf(PropertiesUtil.getProperty("ftp.port"));
//ftp登錄賬號
private static String USERNAME = PropertiesUtil.getProperty("ftp.user");
//ftp登錄密碼
private static String PASSWORD = PropertiesUtil.getProperty("ftp.pass");
private static FTPClient ftpClient = null;
/**
* 初始化ftp服務器
*/
private static void initFtpClient() {
ftpClient = new FTPClient();
ftpClient.setControlEncoding("utf-8");
try {
log.info("connecting...ftp服務器:" + HOST_NAME + ":" + PORT);
//連接ftp服務器
ftpClient.connect(HOST_NAME, PORT);
//登錄ftp服務器
ftpClient.login(USERNAME, PASSWORD);
ftpClient.sendCommand("OPTS UTF8", "ON");
//是否成功登錄服務器
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
log.info("connect failed...ftp服務器:" + HOST_NAME + ":" + PORT);
} else {
log.info("connect successful...ftp服務器:" + HOST_NAME + ":" + PORT);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 上傳文件
*
* @param pathname ftp服務保存地址
* @param fileName 上傳到ftp的文件名
* @param originfilename 待上傳文件的名稱(絕對地址) *
* @return
*/
public static boolean uploadFile(String pathname, String fileName, String originfilename) {
boolean flag = false;
InputStream inputStream = null;
try {
log.info("開始上傳文件");
inputStream = new FileInputStream(new File(originfilename));
initFtpClient();
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
CreateDirecroty(pathname);
ftpClient.makeDirectory(pathname);
ftpClient.changeWorkingDirectory(pathname);
ftpClient.enterLocalPassiveMode();
ftpClient.storeFile(fileName, inputStream);
inputStream.close();
ftpClient.logout();
flag = true;
log.info("上傳文件成功");
} catch (Exception e) {
log.info("上傳文件失敗");
e.printStackTrace();
} 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;
}
/**
* 上傳文件
* @param pathname ftp服務保存地址
* @param file 文件
* @param fileName 上傳到ftp的文件名
* @return
*/
public static boolean uploadFile(String pathname, MultipartFile file, String fileName) {
boolean flag = false;
try {
initFtpClient();
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
CreateDirecroty(pathname);
ftpClient.makeDirectory(pathname);
ftpClient.changeWorkingDirectory(pathname);
ftpClient.enterLocalPassiveMode();
ftpClient.storeFile(fileName, file.getInputStream());
ftpClient.logout();
flag = true;
log.info("上傳文件成功");
} catch (Exception e) {
log.info("上傳文件失敗");
e.printStackTrace();
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return flag;
}
/**
* 下載文件
*
* @param pathName
* @param filename
* @param out
* @return
*/
public static boolean downloadFile(String pathName, String filename, OutputStream out) {
try {
log.info("開始下載文件");
initFtpClient();
//切換FTP目錄
ftpClient.changeWorkingDirectory(pathName);
ftpClient.enterLocalPassiveMode();
FTPFile[] ftpFiles = ftpClient.listFiles();
for (FTPFile file : ftpFiles) {
if (filename.equalsIgnoreCase(file.getName())) {
InputStream inputStream = ftpClient.retrieveFileStream(filename);
byte[] buffer = new byte[1024 * 4];
int n = 0;
while ((n = inputStream.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
out.flush();
}
}
ftpClient.logout();
log.info("下載文件成功");
return true;
} catch (Exception e) {
log.info("下載文件失敗");
e.printStackTrace();
return false;
} finally {
try {
out.close();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 刪除文件 *
*
* @param pathname FTP服務器保存目錄 *
* @param filename 要刪除的文件名稱 *
* @return
*/
public static boolean deleteFile(String pathname, String filename) {
boolean flag = false;
try {
log.info("開始刪除文件");
initFtpClient();
//切換FTP目錄
ftpClient.changeWorkingDirectory(pathname);
ftpClient.dele(filename);
ftpClient.logout();
flag = true;
log.info("刪除文件成功");
} catch (Exception e) {
log.info("刪除文件失敗");
e.printStackTrace();
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return flag;
}
/**
* 改變目錄路徑
*/
public static boolean changeWorkingDirectory(String directory) {
boolean flag = true;
try {
flag = ftpClient.changeWorkingDirectory(directory);
if (flag) {
log.info("進入文件夾" + directory + " 成功!");
} else {
log.info("進入文件夾" + directory + " 失敗!開始創建文件夾");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
return flag;
}
/**
* 創建多層目錄文件,如果有ftp服務器已存在該文件,則不創建,如果無,則創建
*
* @param remote
* @return
* @throws IOException
*/
public static boolean CreateDirecroty(String remote) throws IOException {
boolean success = true;
String directory = remote + "/";
// 如果遠程目錄不存在,則遞歸創建遠程服務器目錄
if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(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 = remote.substring(start, end);
path = path + "/" + subDirectory;
if (!existFile(path)) {
if (makeDirectory(subDirectory)) {
changeWorkingDirectory(subDirectory);
} else {
log.info("創建目錄[" + subDirectory + "]失敗");
changeWorkingDirectory(subDirectory);
}
} else {
changeWorkingDirectory(subDirectory);
}
paths = paths + "/" + subDirectory;
start = end + 1;
end = directory.indexOf("/", start);
// 檢查所有目錄是否創建完畢
if (end <= start) {
break;
}
}
}
return success;
}
/**
* 判斷ftp服務器文件是否存在
*
* @param path
* @return
* @throws IOException
*/
public static boolean existFile(String path) throws IOException {
boolean flag = false;
FTPFile[] ftpFileArr = ftpClient.listFiles(path);
if (ftpFileArr.length > 0) {
flag = true;
}
return flag;
}
/**
* 創建目錄
*/
public static boolean makeDirectory(String dir) {
boolean flag = true;
try {
flag = ftpClient.makeDirectory(dir);
if (flag) {
log.info("創建文件夾" + dir + " 成功!");
} else {
log.info("創建文件夾" + dir + " 失敗!");
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
}
此外,保持連接的同時,需要對多個FTP文件進行操作,一定要調用completePendingCommand()釋放,否則FTP會斷開連接
//操作多文件時候,必須調用completePendingCommand釋放,否則FTP會斷開連接
ftpClient.completePendingCommand();