工具类:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.util.List;
import java.util.Objects;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
/**
* FTP工具类
* @author zql
* @createTime 2020-12-01 21:10:38
* @version 1.1
* @modifyLog 1.1 优化代码
*
*/
public class FtpUtil {
/**
* FTP主机服务器
*/
private static String FTP_HOST = "192.168.1.101";
/**
* FTP登录用户名
*/
private static String FTP_USER_NAME = "ftpservice";
/**
* FTP 登录密码
*/
private static String FTP_PASSWORD = "ftp_service";
/**
* FTP端口
*/
private static int FTP_PORT = 2121;
/**
* 本地字符编码
*/
private static String LOCAL_CHARSET = "GBK";
/**
* ftp服务器对象
*/
private static FTPClient ftpClient = null;
/**
* 开启FTP服务器连接
* @author zql
* @createTime 2020-12-01 21:41:49
*
*/
public static void openConnect() {
try {
FtpUtil.ftpClient = new FTPClient();
// 连接FTP服务器
FtpUtil.ftpClient.connect(FtpUtil.FTP_HOST, FtpUtil.FTP_PORT);
// 登陆FTP服务器
FtpUtil.ftpClient.login(FtpUtil.FTP_USER_NAME, FtpUtil.FTP_PASSWORD);
if (!FTPReply.isPositiveCompletion(FtpUtil.ftpClient.getReplyCode())) {
System.out.println("未连接到FTP,用户名或密码错误");
FtpUtil.ftpClient.disconnect();
} else {
System.out.println("FTP连接成功");
}
} catch (SocketException e) {
e.printStackTrace();
System.out.println("FTP的IP地址可能错误,请正确配置");
} catch (IOException e) {
e.printStackTrace();
System.out.println("FTP的端口错误,请正确配置");
}
}
/**
* 关闭FTP服务器连接
* @author zql
* @createTime 2020-12-01 21:41:27
*
*/
public static void closeConnect() {
if (FtpUtil.ftpClient.isConnected()) {
try {
FtpUtil.ftpClient.disconnect();
System.out.println("FTP关闭成功");
} catch (IOException ioe) {
System.out.println("FTP关闭失败");
}
}
}
/**
* 判断是否开启FTP连接,如果FTPClient对象为NULL或未开启则抛出异常
* @author zql
* @createTime 2020-12-01 21:41:17
*
* @throws Exception
*/
public static void isOpenConnect() throws Exception {
if (Objects.isNull(FtpUtil.ftpClient) || !FtpUtil.ftpClient.isConnected()) {
throw new Exception("FTP连接未开启");
}
}
/**
* 切换FTP路径
* @author zql
* @createTime 2020-12-01 21:41:03
*
* @param path
* @throws Exception
*/
public static void changeWorkingDirectory(String path) throws Exception {
boolean bl = FtpUtil.ftpClient.changeWorkingDirectory(path);
if (!bl) {
throw new Exception("未找到该路径:" + path);
}
}
/**
* 从FTP服务器下载文件
* @author zql
* @createTime 2020-12-01 21:40:45
*
* @param ftpPath FTP服务器文件路径,格式:/root/test
* @param localPath 本地保存文件的路径,格式:E:/test
* @param fileName FTP服务器文件名,格式:text.txt
*/
public static void downloadFtpFile(String ftpPath, String localPath, String fileName) {
try {
FtpUtil.isOpenConnect();
// 中文支持
FtpUtil.ftpClient.setControlEncoding("UTF-8");
FtpUtil.ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
/*
* 这里修改为被动模式 主动模式:客户端开放端口给服务端用; 被动模式:服务端开放端口给客户端用。
* 由于很多客户端在防火墙内,开放端口给服务器端用比较困难。所以用被动模式的时候比较多。
*/
FtpUtil.ftpClient.enterLocalPassiveMode();
FtpUtil.ftpClient.changeWorkingDirectory(ftpPath);
File localFile = new File(localPath + File.separatorChar + fileName);
OutputStream os = new FileOutputStream(localFile);
FtpUtil.ftpClient.retrieveFile(fileName, os);
os.close();
FtpUtil.ftpClient.logout();
} catch (FileNotFoundException e) {
System.out.println("没有找到" + ftpPath + "文件");
e.printStackTrace();
} catch (SocketException e) {
System.out.println("连接FTP失败");
e.printStackTrace();
} catch (IOException e) {
System.out.println("文件读取错误");
e.printStackTrace();
} catch (Exception e) {
System.out.println("FTP连接未开启");
e.printStackTrace();
}
}
/**
* 向FTP服务器上传文件
* @author zql
* @createTime 2020-12-01 21:40:21
*
* @param ftpPath FTP服务器中文件所在路径 格式: /root/test或root/test/
* @param fileName ftp文件名称
* @param input 文件流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile(String ftpPath, String fileName, InputStream input) {
boolean success = false;
try {
FtpUtil.isOpenConnect();
int reply = FtpUtil.ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
FtpUtil.ftpClient.disconnect();
return success;
}
// 查看服务器的编码格式是UTF-8还是GBK
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
if (FTPReply.isPositiveCompletion(FtpUtil.ftpClient.sendCommand("OPTS UTF8", "ON"))) {
FtpUtil.LOCAL_CHARSET = "UTF-8";
}
// 中文支持
FtpUtil.ftpClient.setControlEncoding(FtpUtil.LOCAL_CHARSET);
// ftp上传文件是以文本形式传输的,所以多媒体文件会失真,需要转为二进制形式传输
FtpUtil.ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
/*
* 这里修改为被动模式 主动模式:客户端开放端口给服务端用; 被动模式:服务端开放端口给客户端用。
* 由于很多客户端在防火墙内,开放端口给服务器端用比较困难。所以用被动模式的时候比较多。
*/
FtpUtil.ftpClient.enterLocalPassiveMode();
FtpUtil.createDirectory(FtpUtil.ftpClient, ftpPath);
FtpUtil.ftpClient.storeFile(fileName, input);
input.close();
FtpUtil.ftpClient.logout();
success = true;
} catch (IOException e) {
e.printStackTrace();
System.out.println("FTP服务器上传文件失败:" + e.getMessage());
} catch (Exception e) {
System.out.println("FTP连接未开启");
e.printStackTrace();
} finally {
FtpUtil.closeConnect();
}
return success;
}
/**
* 删除ftp上的文件
* @author zql
* @createTime 2020-12-01 21:39:46
*
* @param filePath 文件路径(后边需要跟上文件名)
* @return
* @throws Exception
*/
public static boolean deleteFtpFile(String filePath) throws Exception {
FtpUtil.isOpenConnect();
// 修改为被动模式
FtpUtil.ftpClient.enterLocalPassiveMode();
// 删除文件
return FtpUtil.ftpClient.deleteFile(filePath);
}
/**
* 删除ftp上指定目录的所有文件
* @author zql
* @createTime 2020-12-01 21:32:01
*
* @param path 指定目录,格式:/root/test/
* @param isDelDirectory 是否删除指定目录,true删除,false不删除
* @return
* @throws Exception
*/
public static boolean deleteAllFtpFile(String path,boolean isDelDirectory) throws Exception {
FtpUtil.isOpenConnect();
boolean bl = false;
// 修改为被动模式
FtpUtil.ftpClient.enterLocalPassiveMode();
// 更换目录到当前目录
FtpUtil.changeWorkingDirectory(path);
FTPFile[] files = FtpUtil.ftpClient.listFiles();
// 删除所有文件
for (FTPFile file : files) {
if (file.isFile()) {
bl = FtpUtil.ftpClient.deleteFile(path + file.getName());
} else if (file.isDirectory() && FtpUtil.isContext(file.getName())) {
bl = FtpUtil.deleteAllFtpFile(path + file.getName() + "/", true);
}
// 如果删除失败直接结束返回
if (!bl) {
return bl;
}
}
if (isDelDirectory) {
// 更换目录到当前目录
FtpUtil.changeWorkingDirectory(path);
FTPFile[] list = FtpUtil.ftpClient.listFiles();
// 目录下没有文件
if (list.length == 0) {
// 删除目录
bl = FtpUtil.ftpClient.deleteFile(path);
}
}
return bl;
}
/**
* 下载/浏览器预览需要的流信息
* @author zql
* @createTime 2020-12-01 21:31:26
*
* @param filePath
* @return
* @throws Exception
*/
public static InputStream getFtpFileStream(String filePath) throws Exception {
FtpUtil.isOpenConnect();
InputStream inputStream = FtpUtil.ftpClient.retrieveFileStream(filePath);
return inputStream;
}
/**
* 判断ftp服务器文件是否存在
* @author zql
* @createTime 2020-12-01 21:31:11
*
* @param path
* @return
* @throws Exception
*/
public static boolean existFile(String path) throws Exception {
FtpUtil.isOpenConnect();
boolean flag = false;
/*
* 如果传入的path是目录(格式root/test),FTPFile得到的就是该目录下的子目录及文件列表,
* 如果传入的path是文件(格式root/test/test.txt),FTPFile得到的就是该文件。
*/
FTPFile[] ftpFileArr = FtpUtil.ftpClient.listFiles(path);
if (ftpFileArr.length > 0) {
flag = true;
}
return flag;
}
/**
* 获得ftp服务器文件的最后修改时间,返回格式:YYYYMMDDhhmmss
* @author zql
* @createTime 2020-12-01 21:30:51
*
* @param path
* @return
* @throws Exception
*/
public static String getModificationTime(String path) throws Exception {
FtpUtil.isOpenConnect();
return FtpUtil.ftpClient.getModificationTime(path);
}
/**
* 递归遍历出FTP目录下面所有文件
* @author zql
* @createTime 2020-12-01 21:26:10
*
* @param pathName 需要遍历的目录,必须以"/"开始和结束
* @param filePathList 用于保存文件路径的列表
* @throws Exception
*/
public static void FTPFilePathList(String pathName, List<String> filePathList) throws Exception {
FtpUtil.FTPFilePathList(pathName, filePathList, null);
}
/**
* 递归遍历目录下面指定的文件名
* @author zql
* @createTime 2020-12-01 21:15:57
*
* @param pathName 需要遍历的目录,必须以"/"开始和结束
* @param filePathList 用于保存文件路径的列表
* @param suffix 文件的扩展名,例如:txt
* @throws Exception
*/
public static void FTPFilePathList(String pathName, List<String> filePathList, String suffix) throws Exception {
FtpUtil.isOpenConnect();
String with = "/";
if (pathName.startsWith(with) && pathName.endsWith(with)) {
// 更换目录到当前目录
FtpUtil.ftpClient.changeWorkingDirectory(pathName);
FTPFile[] files = FtpUtil.ftpClient.listFiles();
boolean flag = true;
for (FTPFile file : files) {
if (Objects.nonNull(suffix)) {
flag = file.getName().endsWith(suffix);
}
if (file.isFile() && flag) {
filePathList.add(pathName + file.getName());
} else if (file.isDirectory() && FtpUtil.isContext(file.getName())) {
FtpUtil.FTPFilePathList(pathName + file.getName() + "/", filePathList, suffix);
}
}
}
}
/**
* 新建FTP目录并切换当前的工作目录为新建的目录
* @author zql
* @createTime 2020-12-01 21:28:11
*
* @param ftpClient
* @param path FTP服务器中文件所在路径 格式: root/test或root/test/
*/
private static void createDirectory(FTPClient ftpClient, String path) {
try {
String with = "/";
// 切换工作目录为根目录
ftpClient.changeWorkingDirectory(with);
// 如果输入的路径不为空或者不为根路径,则转换操作目录
if (!(Objects.isNull(path) || path.length() == 0 || with.equals(path))) {
// 循环创建多级目录
String directory = path.endsWith(with) ? path : path + with;
// 目录不存在
boolean isExistDirectory = !ftpClient.changeWorkingDirectory(new String(directory.getBytes("GBK"), "iso-8859-1"));
// 非根目录
boolean isRoot = !with.equalsIgnoreCase(directory);
// 如果远程目录不存在,则递归创建远程服务器目录
if (isRoot && isExistDirectory) {
// 检测字符串是否以“/”的前缀开始
int start = directory.startsWith(with) ? 1 : 0;
// 返回从 start
// 位置开始查找“/”字符在字符串directory中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
int end = directory.indexOf(with, start);
while (end > start) {
String subDirectory = new String(path.substring(start, end).getBytes("GBK"), "iso-8859-1");
// 不存在子目录
if (!ftpClient.changeWorkingDirectory(subDirectory)) {
// makeDirectory函数只能创建一级目录,不能创建多级目录
if (ftpClient.makeDirectory(subDirectory)) {
// 切换到创建的子目录
ftpClient.changeWorkingDirectory(subDirectory);
} else {
System.out.println("创建目录失败");
}
}
start = end + 1;
end = directory.indexOf(with, start);
}
}
// 切换当前的工作目录为新建的目录
FtpUtil.changeWorkingDirectory(path);
}
} catch (Exception e) {
System.out.println("FTP上传文件进行创建目录结构的时候出现了异常:" + e.getMessage());
}
}
/**
* 递归需要加此判断。否则,ftp默认将‘项目文件所在目录之下的目录(./)’与‘项目文件所在目录向上一级目录下的目录(../)’都纳入递归,这样下去就陷入一个死循环了。需将其过滤掉。
* @author zql
* @createTime 2020-12-01 21:34:59
*
* @param fileName
* @return
*/
private static boolean isContext(String fileName) {
return !".".equals(fileName) && !"..".equals(fileName);
}
}
测试类:
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* FTP工具测试类
*
* @author zql
* @version 1.0
* @createTime 2021-01-02 15:16:00
* @modifyLog
*/
public class FtpUtilTest {
@Before
public void openConnect() {
FtpUtil.openConnect();
}
@After
public void closeConnect() {
FtpUtil.closeConnect();
}
/**
* 下载文件测试
*/
@Test
public void downloadFtpFile() {
String ftpPath = "/root/test/";
String localPath = "E:/test";
String fileName = "test2.txt";
FtpUtil.downloadFtpFile(ftpPath, localPath, fileName);
}
/**
* 上传文件测试
*
* @throws Exception
*/
@Test
public void uploadFile() throws FileNotFoundException {
String ftpPath = "/root/test/";
String localPath = "E:/test/test.txt";
String fileName = "test1.txt";
// 上传一个文件
FileInputStream in = new FileInputStream(new File(localPath));
boolean isUpload = FtpUtil.uploadFile(ftpPath, fileName, in);
if (isUpload) {
System.out.println("文件上传成功。");
} else {
System.out.println("文件上传失败。");
}
}
/**
* 删除文件测试
*
* @throws Exception
*/
@Test
public void deleteFtpFile() throws Exception {
boolean bl = FtpUtil.deleteFtpFile("/root/test/test1.txt");
if (bl) {
System.out.println("成功删除文件");
} else {
System.out.println("删除文件失败");
}
}
/**
* 删除所有文件测试
*
* @throws Exception
*/
@Test
public void deleteAllFtpFile() throws Exception {
boolean bl = FtpUtil.deleteAllFtpFile("/root/test/", true);
FtpUtil.closeConnect();
if (bl) {
System.out.println("成功删除所有文件");
} else {
System.out.println("删除所有文件失败");
}
}
/**
* 获取所有文件路径测试
*
* @throws Exception
*/
@Test
public void FTPFilePathList() throws Exception {
String ftpPath = "/root/";
List<String> filePath = new ArrayList<String>();
FtpUtil.FTPFilePathList(ftpPath, filePath);
for (String s : filePath) {
System.out.println(s);
}
}
/**
* 获取指定文件路径测试
*
* @throws Exception
*/
@Test
public void getSpecifiFilePath() throws Exception {
String ftpPath = "/root/";
List<String> filePath = new ArrayList<String>();
FtpUtil.FTPFilePathList(ftpPath, filePath, "txt");
for (String s : filePath) {
System.out.println(s);
}
}
}
普通项目需要引入的包
commons-net-3.6.jar
maven项目依赖
<!-- FTP maven依赖 -->
<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>