背景
之前写过java调用ftpClient实现大文件传输的文章:ubuntu20-04配置ftp服务器-java通过ftpClient处理文件示例
文章中代码里简单的用了FTPUtil 类每次连接ftp的时候都要new一个新的ftpClient对象,连接登录,用完都要断开连接等操作,在实际应用中往往会遇到性能问题。所以可以利用apache提供的common-pool2里的对象池来做一个ftpClient的对象池。
池化技术主要涉及到下面几个东西:
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
思路步骤
一般写一个对象池可以分为以下几步:
1.构建配置类
2.构建工厂类
3.构建对象池类
4.构建业务template类
构建FtpProperties配置类
配置类里主要配置一些ftp需用用到的参数,比如用户名、密码、ftp服务器地址、端口等等
import javax.annotation.PostConstruct;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
@ConfigurationProperties(FtpProperties.PREFIX)
public class FtpProperties {
public static final String PREFIX = "ftp";
@Autowired
private Environment environment;
@PostConstruct
public void init() {
if (StringUtils.isEmpty(path)) {
this.setPath(environment.resolvePlaceholders("${spring.application.name:}"));
}
requireNotNull(this.domain);
requireNotNull(this.server);
requireNotNull(this.port);
requireNotNull(this.path);
requireNotNull(this.username);
requireNotNull(this.password);
}
/**
* 服务器域名
*/
private String domain;
/**
* 服务器内部域名
*/
private String innerDomain;
/**
* 服务器外部域名
*/
private String outerDomain;
/**
* 服务器IP地址
*/
private String server;
/**
* 服务器端口
*/
private int port;
/**
* 服务器目录
*/
private String path;
/**
* 登陆用户名
*/
private String username;
/**
* 登陆密码
*/
private String password;
/**
* 被动模式
*/
private boolean passiveMode = false;
/**
* 连接池配置
*/
private Pool pool = new Pool();
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getInnerDomain() {
return innerDomain;
}
public void setInnerDomain(String innerDomain) {
this.innerDomain = innerDomain;
}
public String getOuterDomain() {
return outerDomain;
}
public void setOuterDomain(String outerDomain) {
this.outerDomain = outerDomain;
}
public String getServer() {
return server;
}
public void setServer(String server) {
this.server = server;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isPassiveMode() {
return passiveMode;
}
public void setPassiveMode(boolean passiveMode) {
this.passiveMode = passiveMode;
}
public Pool getPool() {
return pool;
}
public void setPool(Pool pool) {
this.pool = pool;
}
public static class Pool {
private int maxTotal = 100;
private int minIdle = 2;
private int maxIdle = 5;
private int maxWaitMillis = 3000;
public int getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaxWaitMillis() {
return maxWaitMillis;
}
public void setMaxWaitMillis(int maxWaitMillis) {
this.maxWaitMillis = maxWaitMillis;
}
}
private void requireNotNull(Object obj) {
if (obj == null) throw new NullPointerException();
}
}
具体配置写入到nacos里的配置文件中:
ftp:
domain: https://xxx
innerDomain: https://xxx
outerDomain: https://xxx
server: 192.168.0.179
port: 21
username: ftpuser
password: ftpuser
pool:
maxTotal: 100
minIdle: 2
maxIdle: 5
maxWaitMillis: 3000
项目启动时通过@PostConstruct注解,可以直接运行上面的init方法来初始化nacos中的配置信息。
注:@PostConstruct 一般用在非静态void方法上,启动时加载,一般的加载顺序是这样:构造方法 ——> @Autowired —— > @PostConstruct ——> 静态方法 (按此顺序加载)
构建FtpClientFactory工厂类
实现org.apache.commons.pool2.PooledObjectFactory接口构建工厂,用于 生产,销毁,激活,验证 我们的池化资源对象。
import java.io.IOException;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import ly.mp.iov.ftp.config.FtpProperties;
/**
*
* FTP客户端工厂类
*
*/
public class FtpClientFactory implements PooledObjectFactory<FTPClient> {
private static final String ROOT_DIR = "/";
private static final int BUFFER_SIZE = 1024 * 1024;
private FtpProperties ftpProperties;
public FtpClientFactory(FtpProperties ftpProperties) {
this.ftpProperties = ftpProperties;
}
@Override
public PooledObject<FTPClient> makeObject() throws Exception {
FTPClient ftpClient = new FTPClient();
return new DefaultPooledObject<>(ftpClient);
}
@Override
public void destroyObject(PooledObject<FTPClient> pooledObject) throws Exception {
FTPClient ftpClient = pooledObject.getObject();
try {
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException e) {
throw new RuntimeException("Could not disconnect from server.", e);
}
}
@Override
public boolean validateObject(PooledObject<FTPClient> pooledObject) {
FTPClient ftpClient = pooledObject.getObject();
try {
return ftpClient.sendNoOp();
} catch (IOException e) {
return false;
}
}
@Override
public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {
FTPClient ftpClient = pooledObject.getObject();
ftpClient.connect(ftpProperties.getServer(), ftpProperties.getPort());
ftpClient.setControlEncoding("UTF-8");
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
if (!ftpClient.login(ftpProperties.getUsername(), ftpProperties.getPassword())) {
throw new RuntimeException("Could not login to server.");
}
if (ftpProperties.isPassiveMode()) {
ftpClient.enterLocalPassiveMode();
}
/**
* 开启缓存,大幅提高传输速度
*/
ftpClient.setBufferSize(BUFFER_SIZE);
}
@Override
public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception {
FTPClient ftpClient = pooledObject.getObject();
try {
ftpClient.changeWorkingDirectory(ROOT_DIR);
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (IOException e) {
throw new RuntimeException("Could not disconnect from server.", e);
}
}
public FtpProperties getFtpProperties() {
return ftpProperties;
}
}
上面实现了PooledObjectFactory中的下面几个方法:
1.生产 makeObject 方法
用于对象的新建,一般是 new 出来之后包装一下。而什么时候需要新建呢,根据策略不同则时机不同。例如在没有闲置资源对象,且已存在的资源数不超过所设置的最大资源时新建。
2.销毁 destroyObject 方法:销毁一个对象,除了很容易想到的闲置过长时间被清理掉了导致需要销毁之外,还有如果进行了第三个方法且返回了 false ,那么也是需要销毁的。
3.验证 validateObject方法:检验这个对象是否还有有效,借出和归还时,以及内置后台线程检测闲置情况时,可以通过验证可以去除一些不符合业务逻辑的资源对象。默认这个方法是不被调用的,要开启则需要在PoolConfig中设置setTestOnBorrow , setTestOnReturn , setTestWhileIdle等属性。
4.激活 activeObject 方法: 在借用一个对象的时候调用,则可以在此重置其内部状态,那么返回的对象就像新的一样
- passivateObject 方法: 对应 activateObject 方法,是在归还一个对象的时候调用,注意不应与activateObject方法有业务逻辑上的冲突
这些是构建工厂类都会有的固式方法。
构建FtpClientPool对象池类
通过GenericObjectPool里的borrowObject可以借用一个对象,returnObject可以归还一个对象,这样ftpClient对象就由GenericObjectPool对象池管理了起来
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import ly.mp.iov.ftp.config.FtpProperties;
/**
*
* FTP客户端连接池
*
*/
public class FtpClientPool {
private FtpClientFactory ftpClientFactory;
private final GenericObjectPool<FTPClient> internalPool;
public FtpClientPool(FtpClientFactory ftpClientFactory){
this.ftpClientFactory = ftpClientFactory;
FtpProperties properties = ftpClientFactory.getFtpProperties();
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(properties.getPool().getMaxTotal());
poolConfig.setMinIdle(properties.getPool().getMinIdle());
poolConfig.setMaxIdle(properties.getPool().getMaxIdle());
poolConfig.setMaxWaitMillis(properties.getPool().getMaxWaitMillis());
this.internalPool = new GenericObjectPool<FTPClient>(ftpClientFactory, poolConfig);
}
public FTPClient getFtpClient() {
try {
return internalPool.borrowObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void returnFtpClient(FTPClient ftpClient) {
internalPool.returnObject(ftpClient);
}
public void destroy() {
internalPool.close();
}
public FtpClientFactory getFtpClientFactory() {
return ftpClientFactory;
}
}
构建FtpTemplate业务接口类
对象池构建好之后就可以封装业务接口了
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ly.mp.iov.ftp.config.FtpProperties;
import ly.mp.iov.ftp.exception.RemoteFileExistException;
import ly.mp.iov.ftp.util.FtpClientUtils;
public class FtpTemplate {
private static final Logger logger = LoggerFactory.getLogger(FtpTemplate.class);
private FtpProperties ftpProperties;
private FtpClientPool ftpClientPool;
public FtpTemplate(FtpClientPool ftpClientPool) {
this.ftpClientPool = ftpClientPool;
this.ftpProperties = ftpClientPool.getFtpClientFactory().getFtpProperties();
}
/**
* 读取FTP文件
* @param path
* @param out
* @return 读取成功
*/
public boolean read(String path, OutputStream out) {
FTPClient ftpClient = ftpClientPool.getFtpClient();
try {
if (!FtpClientUtils.exists(path, ftpClient)) {
logger.info("【{}】文件不存在", path);
return false;
}
return ftpClient.retrieveFile(path, out);
} catch (IOException e) {
throw new RuntimeException("文件读取失败");
} finally {
ftpClientPool.returnFtpClient(ftpClient);
}
}
/**
* 上传文件到FTP服务器
* @param file 本地文件路径
* @param fileName
* @return
*/
public FtpFile write(String file, String fileName) {
try (InputStream in = new FileInputStream(file)){
return write(in, fileName);
} catch (FileNotFoundException e) {
throw new RuntimeException("文件不存在:" + file);
} catch (IOException e) {
throw new RuntimeException("文件关闭失败");
}
}
/**
* 上传文件到FTP服务器
* @param in
* @param fileName
* @param filePath
* @return
*/
public FtpFile write(String file, String fileName, String filePath) {
try (InputStream in = new FileInputStream(file)){
return write(in, fileName, filePath);
} catch (FileNotFoundException e) {
throw new RuntimeException("文件不存在:" + file);
} catch (IOException e) {
throw new RuntimeException("文件关闭失败");
}
}
/**
* 上传文件流到FTP服务器
* @param in
* @param fileName
* @return
*/
public FtpFile write(InputStream in, String fileName) {
String filePath = ftpProperties.getPath() + "/"
+ DateFormatUtils.format(new Date(), "yyyyMM") + "/" + FtpClientUtils.UUID() + "/";
return write(in, fileName, filePath);
}
/**
* 上传文件流到FTP服务器
* @param in 文件流
* @param fileName 文件名
* @param filePath 文件路径
* @return
*/
public FtpFile write(InputStream in, String fileName, String filePath) {
if (!validateFilePath(filePath)) {
throw new IllegalArgumentException("文件路径必须在当前服务路径下");
}
FTPClient ftpClient = ftpClientPool.getFtpClient();
try {
// 创建并移动到指定目录
String path = FtpClientUtils.trimPath(filePath);
try {
FtpClientUtils.makeDirectory(path, ftpClient);
} catch (IOException e) {
throw new RuntimeException("FTP服务器创建目录失败", e);
}
try {
FtpClientUtils.storeFile(in, fileName, ftpClient);
} catch (RemoteFileExistException e) {
throw new RemoteFileExistException(String.format("【%s】文件已存在", "/" + path + fileName));
}
FtpFile ftpFile = new FtpFile();
ftpFile.setInnerPath(ftpProperties.getInnerDomain() + "/" + path + fileName);
ftpFile.setOuterPath(ftpProperties.getOuterDomain() + "/" + path + fileName);
return ftpFile;
} finally {
ftpClientPool.returnFtpClient(ftpClient);
}
}
/**
* 删除FTP服务器上指定文件,仅删除文件
* @param filePath
* @return
*/
public boolean delete(String filePath) {
return delete(filePath, FileFilter.FILE);
}
/**
* 删除FTP服务器上指定文件,可以指定删除文件、文件夹还是所有
* @param filePath
* @return
*/
public boolean delete(String filePath, FileFilter fileFilter) {
if (!validateFilePath(filePath)) {
throw new IllegalArgumentException("删除文件路径必须在当前服务路径下");
}
FTPClient ftpClient = ftpClientPool.getFtpClient();
try {
return FtpClientUtils.deleteFile(filePath, ftpClient, fileFilter);
} finally {
ftpClientPool.returnFtpClient(ftpClient);
}
}
/**
* 判断文件是否存在
* @param filePath
* @return
*/
public boolean exists(String filePath) {
FTPClient ftpClient = ftpClientPool.getFtpClient();
try {
return FtpClientUtils.exists(filePath, ftpClient);
} finally {
ftpClientPool.returnFtpClient(ftpClient);
}
}
/**
* 获取FTP客户端,使用必须调用{@code #getFtpClient()}方法
* @return
*/
public FTPClient getFtpClient() {
return ftpClientPool.getFtpClient();
}
/**
* 归还FTP客户端
* @param ftpClient
*/
public void returnFtpClient(FTPClient ftpClient) {
ftpClientPool.returnFtpClient(ftpClient);
}
/**
* 获取配置类
* @return
*/
public FtpProperties getFtpProperties() {
return ftpProperties;
}
/**
* 校验删除文件路径必须在本服务所属路径下
* @param filePath
* @return
*/
private boolean validateFilePath(String filePath) {
return filePath.startsWith("/" + ftpProperties.getPath())
|| filePath.startsWith(ftpProperties.getPath());
}
}
这里面的业务方法可根据自身业务更改增减,本文的侧重点在于演示池化技术的应用来解决ftp的连接性能问题。
最后新建FtpAutoConfiguration把FtpProperties、FtpClientFactory、FtpClientPool、FtpTemplate都注入到spring中:
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import ly.mp.iov.ftp.core.FtpClientFactory;
import ly.mp.iov.ftp.core.FtpClientPool;
import ly.mp.iov.ftp.core.FtpTemplate;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(FtpProperties.class)
@ConditionalOnProperty(name = "ftp.enabled", havingValue="true", matchIfMissing = true)
public class FtpAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public FtpProperties ftpProperties(Environment environment) {
return new FtpProperties();
}
@Bean
public FtpClientFactory ftpClientFactory(FtpProperties ftpProperties) {
return new FtpClientFactory(ftpProperties);
}
@Bean
public FtpClientPool ftpClientPool(FtpClientFactory ftpClientFactory) {
return new FtpClientPool(ftpClientFactory);
}
@Bean
public FtpTemplate ftpTemplate(FtpClientPool ftpClientPool) {
return new FtpTemplate(ftpClientPool);
}
}
其他服务用的时候就可以直接通过@Autowired来使用了:
@Autowired
private FtpTemplate ftpTemplate;
把剩余的用到的业务类也贴一下吧:
/**
*
* 文件过滤枚举类
*
*/
public enum FileFilter {
ALL,
FILE,
DIRECTORY;
}
public class FtpFile {
private String innerPath;
private String outerPath;
public String getInnerPath() {
return innerPath;
}
public void setInnerPath(String innerPath) {
this.innerPath = innerPath;
}
public String getOuterPath() {
return outerPath;
}
public void setOuterPath(String outerPath) {
this.outerPath = outerPath;
}
@Override
public String toString() {
return "FtpFile [innerPath=" + innerPath + ", outerPath=" + outerPath + "]";
}
}
/**
*
* FTP服务器文件已存在异常
*
*/
public class RemoteFileExistException extends RuntimeException {
private static final long serialVersionUID = 5266212078073813671L;
public RemoteFileExistException() {
super();
}
public RemoteFileExistException(String msg) {
super(msg);
}
}
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ly.mp.iov.ftp.core.FileFilter;
import ly.mp.iov.ftp.exception.RemoteFileExistException;
/**
*
* FTP工具类
*/
public class FtpClientUtils {
private static final Logger logger = LoggerFactory.getLogger(FtpClientUtils.class);
/**
* 通配符
*/
private static final String FILE_WILDCARD = "*";
/**
* 文件路径分隔符
*/
private static final String FILE_SEPARATOR = "/";
/**
* 生成UUID
* @return
*/
public static String UUID() {
return java.util.UUID.randomUUID().toString().replaceAll("-", "");
}
/**
* 创建目录并不返回根目录
* @param filePath 文件路径
* @param ftpClient
* @return
*/
public static boolean makeDirectory(String filePath, FTPClient ftpClient) throws IOException {
boolean isOK = false;
String directory = filePath.substring(0, filePath.lastIndexOf(FILE_SEPARATOR) + 1);
if (!directory.equalsIgnoreCase(FILE_SEPARATOR) && !ftpClient.changeWorkingDirectory(directory)) {
int start = 0;
int end = 0;
if (directory.startsWith(FILE_SEPARATOR)) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf(FILE_SEPARATOR, start);
while (true) {
String subDirectory = filePath.substring(start, end);
if (!ftpClient.changeWorkingDirectory(subDirectory)) {
if (ftpClient.makeDirectory(subDirectory)) {
ftpClient.changeWorkingDirectory(subDirectory);
} else {
throw new IOException(String.format("创建[%]->[%]目录失败", filePath, subDirectory));
}
}
start = end + 1;
end = directory.indexOf(FILE_SEPARATOR, start);
if (end <= start) {
isOK = true;
break;
}
}
} else {
isOK = true;
}
return isOK;
}
/**
* 上传文件到指定服务器目录下
* @param in 文件流
* @param fileName 文件名
* @param ftpClient FTP客户端
* @throws RemoteFileExistException FTP服务器文件已存在
* @return
*/
public static void storeFile(InputStream in, String fileName, FTPClient ftpClient) {
// 使用BufferedInputStream大幅提升上传性能
try (BufferedInputStream bufferedIn = new BufferedInputStream(in)) {
// FTP服务器文件名使用ISO-8859-1编码,解决中文乱码问题
String remoteFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
boolean success = ftpClient.storeFile(remoteFileName, bufferedIn);
if (!success) {
throw new RuntimeException("文件上传失败");
}
} catch (IOException e) {
throw new RuntimeException("文件上传失败", e);
}
}
/**
* 递归删除文件和文件夹
* @param filePath
* @param ftpClient
* @param fileFilter
* @return
*/
public static boolean deleteFile(String filePath, FTPClient ftpClient, FileFilter fileFilter) {
FilePath realFilePath = deconstructionFilePath(filePath);
try {
if (StringUtils.isNotEmpty(realFilePath.getFileDir())) {
ftpClient.changeWorkingDirectory(realFilePath.getFileDir());
}
FTPFile[] files = ftpClient.listFiles();
int i = 0;
for (; i < files.length; i++) {
// 跳过当前目录和上级目录
if (".".equals(files[i].getName()) || "..".equals(files[i].getName())) {
continue;
}
if (!FILE_WILDCARD.equals(realFilePath.getFileName()) && !files[i].getName().equals(realFilePath.getFileName())) {
continue;
}
if (files[i].isDirectory() && !FileFilter.FILE.equals(fileFilter)) {
FTPFile[] subFiles = ftpClient.listFiles(files[i].getName());
if (subFiles.length > 2) {
ftpClient.changeWorkingDirectory(files[i].getName());
// 递归删除文件夹下的文件
if (!deleteFile(FILE_WILDCARD, ftpClient, FileFilter.ALL)) {
return false;
}
ftpClient.changeToParentDirectory();
}
return ftpClient.removeDirectory(utf8ToIso(realFilePath.getFileName()));
} else if (files[i].isFile() && !FileFilter.DIRECTORY.equals(fileFilter)) {
return ftpClient.deleteFile(utf8ToIso(files[i].getName()));
}
}
if (i == files.length) {
logger.error("文件删除失败:【{}】文件不存在,文件过滤器【{}】", filePath, fileFilter.name());
return false;
} else {
return true;
}
} catch (IOException e) {
logger.error("文件删除失败:【{}】文件不存在", filePath);
return false;
}
}
/**
* 将文件路径字符串解构为目录和文件名
* @param filePathStr
* @return
*/
private static FilePath deconstructionFilePath(String filePathStr) {
FilePath filePath = new FilePath();
if (filePathStr.endsWith(FILE_SEPARATOR)) {
filePathStr = filePathStr.substring(0, filePathStr.length()-1);
}
if (filePathStr.contains(FILE_SEPARATOR)) {
filePath.setFileDir(filePathStr.substring(0, filePathStr.lastIndexOf('/')));
filePath.setFileName(filePathStr.substring(filePath.getFileDir().length()+1));
} else {
filePath.setFileDir("");
filePath.setFileName(filePathStr);
}
return filePath;
}
/**
* 判断FTP服务器文件是否存在
* @param filePath
* @param ftpClient
* @return
*/
public static boolean exists(String filePath, FTPClient ftpClient) {
FilePath realFilePath = deconstructionFilePath(filePath);
if (StringUtils.isNotEmpty(realFilePath.getFileDir())) {
try {
ftpClient.changeWorkingDirectory(realFilePath.getFileDir());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 先判断FTP服务器上是否已存在的文件
FTPFile[] ftpFiles;
try {
ftpFiles = ftpClient.listFiles();
} catch (IOException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < ftpFiles.length; i++) {
if (ftpFiles[i].getName().equals(realFilePath.getFileName())) {
return true;
}
}
return false;
}
/**
* UTF-8转ISO-8859-1
* @param str
* @return
*/
public static String utf8ToIso(String str) {
try {
// FTP服务器文件名使用ISO-8859-1编码,解决中文乱码问题
return new String(str.getBytes("UTF-8"), "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* ISO-8859-1转UTF-8
* @param str
* @return
*/
public static String isoToUtf8(String str) {
try {
// FTP服务器文件名使用ISO-8859-1编码,解决中文乱码问题
return new String(str.getBytes("ISO-8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* 规范格式文件路径格式
* @param filePath
* @return
*/
public static String trimPath(String filePath) {
StringBuilder filePathBuilder = new StringBuilder(filePath);
if (filePathBuilder.length() > 1 && '/' == filePathBuilder.charAt(0)) {
filePathBuilder.deleteCharAt(0);
}
if (filePathBuilder.length() > 0 && '/' != filePathBuilder.charAt(filePathBuilder.length()-1)) {
filePathBuilder.append('/');
}
return filePathBuilder.toString();
}
static class FilePath {
private String fileDir;
private String fileName;
public String getFileDir() {
return fileDir;
}
public void setFileDir(String fileDir) {
this.fileDir = fileDir;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
}
参考:https://blog.csdn.net/qq_37186947/article/details/104227552