支持FTP工具类

pull/879/head
pcshao 2025-04-15 17:02:32 +08:00
parent 332cf6c5fa
commit 911c4c092b
7 changed files with 582 additions and 0 deletions

View File

@ -8,6 +8,7 @@
<modelVersion>4.0.0</modelVersion>
<properties>
<hutool.version>5.8.35</hutool.version>
<commons-net.version>3.7</commons-net.version>
</properties>
<artifactId>eladmin-common</artifactId>
@ -20,5 +21,11 @@
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- 用于FTP -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version> <!-- 版本号可能会有更新 -->
</dependency>
</dependencies>
</project>

View File

@ -17,9 +17,13 @@ package me.zhengjie.config.properties;
import lombok.Data;
import me.zhengjie.utils.ElConstant;
import me.zhengjie.utils.EncryptUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.io.File;
import java.util.Objects;
/**
* @author Zheng Jie
*/
@ -57,4 +61,76 @@ public class FileProperties {
private String avatar;
}
// region 支持FTP配置
private FtpConfig ftp;
@Data
public static class FtpConfig {
private String host;
private int port;
private String username;
private String password;
private String mainPath;
private int connectTimeout = 5000; // ms
private int dataTimeout = 30000; // ms
// 连接池相关配置
private int minIdle = 1;
private int maxIdle = 3;
private int maxTotal = 5;
private long maxWait = 5000; // ms
public String getPassword() {
String password = this.password;
try {
password = EncryptUtils.desDecrypt(this.password);
} catch (Exception e) {
e.printStackTrace();
}
return password;
}
}
/**
* FTP
*
* @param file
* @return
*/
public String getRelativePath(File file) {
String mainPath = getPath().getPath();
String ftpPath = getFtp().getMainPath();
if (Objects.nonNull(file)) {
if (file.getPath().contains(mainPath)) {
int len = file.getPath().length() - file.getName().length();
String result = file.getPath().substring(mainPath.length(), len);
// 处理分割符
return dealPath(result);
} else if (file.getPath().contains(ftpPath)) {
int len = file.getPath().length() - file.getName().length();
String result = file.getPath().substring(ftpPath.length(), len);
// 处理分割符
return dealPath(result);
}
}
return file.getPath();
}
/**
*
*
* @param path
* @return
*/
private String dealPath(String path) {
String os = System.getProperty("os.name");
if (os.toLowerCase().startsWith(ElConstant.WIN) && path.contains("\\")) {
return path.replace("\\", "/");
} else if (os.toLowerCase().startsWith(ElConstant.MAC)) {
return path;
}
return path;
}
// endregion
}

View File

@ -0,0 +1,149 @@
package me.zhengjie.utils.ftp;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.config.properties.FileProperties;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.function.Function;
/**
* FTP
*
* @author pcshao.cn
* @date 13/04/2024
*/
@Slf4j
@Service
public class FtpConnectionPoolManager {
private volatile static GenericObjectPool<FTPClient> pool = null;
@Resource
private FileProperties fileProperties;
/**
*
*/
public void initPool() {
if (Objects.nonNull(pool))
return;
else {
// 懒加载
synchronized (this) {
if (Objects.nonNull(pool))
return;
}
}
FileProperties.FtpConfig ftp = fileProperties.getFtp();
GenericObjectPoolConfig<FTPClient> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(ftp.getMaxTotal()); // 设置连接池最大连接数
poolConfig.setMaxIdle(ftp.getMaxIdle()); // 设置连接池最大空闲连接数
poolConfig.setMinIdle(ftp.getMinIdle()); // 设置连接池最小空闲连接数
poolConfig.setMaxWaitMillis(ftp.getMaxWait()); // 设置连接池最大等待时间,单位为毫秒
poolConfig.setTestOnBorrow(true); // 设置获取连接前进行检查
pool = new GenericObjectPool<>(new BasePooledObjectFactory<FTPClient>() {
@Override
public FTPClient create() throws Exception {
String server = ftp.getHost();
int port = ftp.getPort();
String username = ftp.getUsername();
String password = ftp.getPassword();
FTPClient ftpClient = new FTPClient();
// 设置控制连接的字符编码为UTF-8
ftpClient.setControlEncoding("UTF-8");
ftpClient.connect(server, port);
ftpClient.login(username, password);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setConnectTimeout(ftp.getConnectTimeout()); // 连接超时时间为5秒
ftpClient.setDataTimeout(ftp.getDataTimeout()); // 数据传输超时时间为30秒
return ftpClient;
}
@Override
public void destroyObject(PooledObject<FTPClient> p) throws Exception {
FTPClient ftpClient = p.getObject();
ftpClient.logout();
ftpClient.disconnect();
}
@Override
public boolean validateObject(PooledObject<FTPClient> p) {
FTPClient ftpClient = p.getObject();
try {
log.warn("FTP连接池管理 检测FTP连接状态 reply:{} status:{} obj:{}", ftpClient.getReplyString(), ftpClient.getStatus(), ftpClient);
return ftpClient.isConnected() && ftpClient.sendNoOp();
} catch (Exception e) {
log.warn("FTP连接池管理 检测FTP连接失败 {}", e.getMessage());
if (log.isDebugEnabled())
log.debug(e.getMessage(), e);
return false;
}
}
@Override
public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
DefaultPooledObject defaultPooledObject = new DefaultPooledObject(ftpClient);
return defaultPooledObject;
}
}, poolConfig);
}
/**
*
*
* @param function
* @param <R>
* @return
* @throws Exception
*/
public <R> R execute(Function<FTPClient, R> function) throws Exception {
FTPClient ftpClient = null;
try {
ftpClient = borrowFTPClient();
return function.apply(ftpClient);
} finally {
returnFTPClient(ftpClient);
}
}
/**
*
*
* @return
* @throws Exception
*/
public FTPClient borrowFTPClient() throws Exception {
initPool();
return pool.borrowObject();
}
/**
*
*
* @param ftpClient
*/
public void returnFTPClient(FTPClient ftpClient) {
initPool();
pool.returnObject(ftpClient);
}
/**
*
*/
public void close() {
if (Objects.isNull(pool))
return;
pool.close();
}
}

View File

@ -0,0 +1,299 @@
package me.zhengjie.utils.ftp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @author pcshao.cn
* @date 20/01/2024
*/
@Slf4j
public class FtpUtil {
/**
* FTP
*
* @param ftpClient
* @param localFilePath
* @param remoteDirectory
* @return
*/
public static boolean uploadFile(FTPClient ftpClient, String localFilePath, String remoteDirectory) {
boolean done = false;
try {
// 检查远程目录是否存在
checkRemoteDir(ftpClient, remoteDirectory);
// 设置远程目录的字符编码为UTF-8
ftpClient.changeWorkingDirectory(new String(remoteDirectory.getBytes(StandardCharsets.UTF_8)));
File localFile = new File(localFilePath);
FileInputStream inputStream = new FileInputStream(localFile);
String remoteFilePath = remoteDirectory + localFile.getName();
done = ftpClient.storeFile(remoteFilePath, inputStream);
if (done) {
log.info("FtpUtil uploadFile successfully.");
} else {
log.error("FtpUtil uploadFile failed. reply: {} remotePath: {} obj: {}", ftpClient.getReplyString(), remoteFilePath, ftpClient);
}
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return done;
}
/**
* FTP
*
* @param server
* @param port
* @param username
* @param password
* @param localFilePath
* @param remoteDirectory
*/
public static boolean uploadFile(String server, int port, String username, String password, String localFilePath, String remoteDirectory) {
boolean done = false;
FTPClient ftpClient = new FTPClient();
try {
// 设置控制连接的字符编码为UTF-8
ftpClient.setControlEncoding("UTF-8");
ftpClient.connect(server, port);
ftpClient.login(username, password);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// 检查远程目录是否存在
checkRemoteDir(ftpClient, remoteDirectory);
// 设置远程目录的字符编码为UTF-8
ftpClient.changeWorkingDirectory(new String(remoteDirectory.getBytes(StandardCharsets.UTF_8)));
File localFile = new File(localFilePath);
FileInputStream inputStream = new FileInputStream(localFile);
String remoteFilePath = remoteDirectory + localFile.getName();
done = ftpClient.storeFile(remoteFilePath, inputStream);
if (done) {
log.info("FtpUtil uploadFile successfully.");
} else {
log.info("FtpUtil uploadFile failed. reply: {} remotePath: {}", ftpClient.getReplyString(), remoteFilePath);
}
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return done;
}
/**
*
*
* @param ftpClient
* @param remoteDirectory
* @throws IOException
*/
private static void checkRemoteDir(FTPClient ftpClient, String remoteDirectory) throws IOException {
FTPFile[] ftpFiles = ftpClient.mlistDir(remoteDirectory);
log.info("FtpUtil mlistDir {} reply: {}", remoteDirectory, ftpClient.getReplyString());
if (ftpFiles.length <= 0) {
ftpClient.makeDirectory(remoteDirectory);
log.info("FtpUtil makeDirectory {} reply: {}", remoteDirectory, ftpClient.getReplyString());
}
}
/**
* FTP
*
* @param ftpClient
* @param remoteFilePath
* @param localDirectory
* @return
*/
public static File downloadFile(FTPClient ftpClient, String remoteFilePath, String localDirectory) {
File localFile = null;
try {
// 设置远程目录的字符编码为UTF-8
String remoteDirectory = remoteFilePath.substring(0, remoteFilePath.lastIndexOf("/") + 1);
ftpClient.changeWorkingDirectory(new String(remoteDirectory.getBytes(StandardCharsets.UTF_8)));
String remoteFileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
localFile = new File(localDirectory + remoteFileName);
OutputStream outputStream = new FileOutputStream(localFile);
boolean success = ftpClient.retrieveFile(remoteFilePath, outputStream);
if (success) {
log.info("FtpUtil downloaded successfully.");
} else {
log.info("FtpUtil download failed. reply: {} remotePath: {}", ftpClient.getReplyString(), remoteFilePath);
return null;
}
outputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
return null;
}
return localFile;
}
/**
* FTP
*
* @param server
* @param port
* @param username
* @param password
* @param remoteFilePath
* @param localDirectory
* @return
*/
public static File downloadFile(String server, int port, String username, String password, String remoteFilePath, String localDirectory) {
File localFile = null;
FTPClient ftpClient = new FTPClient();
try {
// 设置控制连接的字符编码为UTF-8
ftpClient.setControlEncoding("UTF-8");
ftpClient.connect(server, port);
ftpClient.login(username, password);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// 设置远程目录的字符编码为UTF-8
String remoteDirectory = remoteFilePath.substring(0, remoteFilePath.lastIndexOf("/") + 1);
ftpClient.changeWorkingDirectory(new String(remoteDirectory.getBytes(StandardCharsets.UTF_8)));
String remoteFileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
localFile = new File(localDirectory + remoteFileName);
OutputStream outputStream = new FileOutputStream(localFile);
boolean success = ftpClient.retrieveFile(remoteFilePath, outputStream);
if (success) {
log.info("FtpUtil downloaded successfully.");
} else {
log.info("FtpUtil download failed. reply: {} remotePath: {}", ftpClient.getReplyString(), remoteFilePath);
return null;
}
outputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
return null;
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return localFile;
}
/**
* FTP
*
* @param ftpClient
* @param remoteFilePath
* @return
*/
public static boolean deleteFile(FTPClient ftpClient, String remoteFilePath) {
boolean success = false;
try {
// 设置远程目录的字符编码为UTF-8
String remoteDirectory = remoteFilePath.substring(0, remoteFilePath.lastIndexOf("/") + 1);
ftpClient.changeWorkingDirectory(new String(remoteDirectory.getBytes(StandardCharsets.UTF_8)));
String remoteFileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
success = ftpClient.deleteFile(remoteFileName);
if (success) {
log.info("FtpUtil delete successfully.");
} else {
log.info("FtpUtil delete failed. reply: {} remotePath: {}", ftpClient.getReplyString(), remoteFilePath);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return success;
}
/**
* FTP
*
* @param server
* @param port
* @param username
* @param password
* @param remoteFilePath
* @return
*/
public static boolean deleteFile(String server, int port, String username, String password, String remoteFilePath) {
boolean success = false;
FTPClient ftpClient = new FTPClient();
try {
// 设置控制连接的字符编码为UTF-8
ftpClient.setControlEncoding("UTF-8");
ftpClient.connect(server, port);
ftpClient.login(username, password);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
// 设置远程目录的字符编码为UTF-8
String remoteDirectory = remoteFilePath.substring(0, remoteFilePath.lastIndexOf("/") + 1);
ftpClient.changeWorkingDirectory(new String(remoteDirectory.getBytes(StandardCharsets.UTF_8)));
String remoteFileName = remoteFilePath.substring(remoteFilePath.lastIndexOf("/") + 1);
success = ftpClient.deleteFile(remoteFileName);
if (success) {
log.info("FtpUtil delete successfully.");
} else {
log.info("FtpUtil delete failed. reply: {} remotePath: {}", ftpClient.getReplyString(), remoteFilePath);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
} finally {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return success;
}
}

View File

@ -0,0 +1,23 @@
package me.zhengjie.utils;
import me.zhengjie.utils.ftp.FtpUtil;
import org.junit.jupiter.api.Test;
/**
* @author pcshao.cn
* @date 2025/3/12
*/
public class FtpUtilTest {
@Test
public void testDowndload() throws Exception {
String password = EncryptUtils.desDecrypt("xxx");
FtpUtil.downloadFile("ftp.xxx.xx", 21, "ftptest", password,
"/test.xlsx", "tempFile" + System.currentTimeMillis() + ".xlsx");
}
public void testUpload() throws Exception {
String password = EncryptUtils.desDecrypt("xxx");
FtpUtil.uploadFile("ftp.xxx.xx", 21, "ftptest", password, "tempFile", "");
}
}

View File

@ -116,3 +116,17 @@ file:
# 文件大小 /M
maxSize: 100
avatarMaxSize: 5
# FTP文件服务器配置
ftp:
mainPath: \home\eladmin\file\
username: ftptest
password: B6890183857ABC452C9E3170906AF9AB97872252D8A4769E
host: pcshao.cn
port: 21
connectTimeout: 5000 # 连接超时时间 ms
dataTimeout: 30000 # 数据传输超时时间 ms
# FTP线程池
minIdle: 1
maxIdle: 3
maxTotal: 5
maxWait: 5000 # ms

View File

@ -127,3 +127,17 @@ file:
# 文件大小 /M
maxSize: 100
avatarMaxSize: 5
# FTP文件服务器配置
ftp:
mainPath: \home\eladmin\file\
username: ftptest
password: B6890183857ABC452C9E3170906AF9AB97872252D8A4769E
host: pcshao.cn
port: 21
connectTimeout: 5000 # 连接超时时间 ms
dataTimeout: 30000 # 数据传输超时时间 ms
# FTP线程池
minIdle: 1
maxIdle: 3
maxTotal: 5
maxWait: 5000 # ms