1、配置文件
package com.faea.bus.core.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author liuchao
* @date 2020-03-28
*/
@Getter
@Setter
@ConfigurationProperties(prefix = FtpProperties.PREFIX, ignoreUnknownFields = false)
public class FtpProperties {
public static final String PREFIX = "faea.ftp";
/**
* Ip
*/
private String host;
/**
* 端口
*/
private Integer port;
/**
* 登录账号
*/
private String name;
/**
* 登录密码
*/
private String password;
/**
* 访问前缀
*/
private String urlPrefix;
/**
* 是否被动模式
*/
private boolean passiveMode = false;
/**
* 编码格式
*/
private String encoding = "UTF-8";
/**
* 连接超时时间
*/
private int connectTimeout = 30000;
/**
* 缓存
*/
private int bufferSize = 8096;
}
2、基于Apache commons-pool2 对象池实现ftpClient 池工厂
package com.faea.bus.core.ftp;
import cn.hutool.core.util.StrUtil;
import com.faea.bus.core.exception.FaeaBusinessException;
import com.faea.bus.core.properties.FtpProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import java.io.IOException;
/**
* Ftp客户端工厂
*
* @author liuchao
* @date 2020/6/28
*/
@Slf4j
public class FtpClientFactory extends BasePooledObjectFactory<FTPClient> {
private FtpProperties ftpProperties;
public FtpClientFactory(FtpProperties ftpProperties) {
this.ftpProperties = ftpProperties;
}
@Override
public FTPClient create() {
final FTPClient ftpClient = new FTPClient();
ftpClient.setControlEncoding(ftpProperties.getEncoding());
ftpClient.setConnectTimeout(ftpProperties.getConnectTimeout());
try {
// 连接ftp服务器
ftpClient.connect(ftpProperties.getHost(), ftpProperties.getPort());
// 登录ftp服务器
ftpClient.login(ftpProperties.getName(), ftpProperties.getPassword());
} catch (IOException e) {
throw new FaeaBusinessException("创建ftp连接失败", e);
}
// 是否成功登录服务器
final int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
try {
ftpClient.disconnect();
} catch (IOException e) {
// ignore
}
throw new FaeaBusinessException(StrUtil.format("Login failed for user [{}], reply code is: [{}]",
ftpProperties.getName(), replyCode));
}
ftpClient.setBufferSize(ftpProperties.getBufferSize());
//设置模式
if (ftpProperties.isPassiveMode()) {
ftpClient.enterLocalPassiveMode();
}
return ftpClient;
}
@Override
public PooledObject<FTPClient> wrap(FTPClient obj) {
return new DefaultPooledObject<>(obj);
}
/**
* 销毁FtpClient对象
*/
@Override
public void destroyObject(PooledObject<FTPClient> ftpPooled) {
if (ftpPooled == null) {
return;
}
FTPClient ftpClient = ftpPooled.getObject();
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
}
} catch (IOException io) {
log.error("ftp client logout failed...{}", io);
} finally {
try {
ftpClient.disconnect();
} catch (IOException io) {
log.error("close ftp client failed...{}", io);
}
}
}
/**
* 验证FtpClient对象是否还可用
*/
@Override
public boolean validateObject(PooledObject<FTPClient> ftpPooled) {
try {
FTPClient ftpClient = ftpPooled.getObject();
return ftpClient.sendNoOp();
} catch (IOException e) {
log.error("Failed to validate client: {}", e);
}
return false;
}
}
3、ftpClient 池
package com.faea.bus.core.ftp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.BaseObjectPool;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* ftp client 池
*
* @author liuchao
* @date 2020/6/28
*/
@Slf4j
public class FtpClientPool extends BaseObjectPool<FTPClient> {
private static final int DEFAULT_POOL_SIZE = 4;
private final BlockingQueue<FTPClient> ftpBlockingQueue;
private final FtpClientFactory ftpClientFactory;
/**
* 初始化连接池,需要注入一个工厂来提供FTPClient实例
*
* @param ftpClientFactory ftp工厂
* @throws Exception
*/
public FtpClientPool(FtpClientFactory ftpClientFactory) throws Exception {
this(DEFAULT_POOL_SIZE, ftpClientFactory);
}
public FtpClientPool(int poolSize, FtpClientFactory factory) throws Exception {
this.ftpClientFactory = factory;
ftpBlockingQueue = new ArrayBlockingQueue<>(poolSize);
initPool(poolSize);
}
/**
* 初始化连接池,需要注入一个工厂来提供FTPClient实例
*
* @param maxPoolSize 最大连接数
* @throws Exception
*/
private void initPool(int maxPoolSize) throws Exception {
for (int i = 0; i < maxPoolSize; i++) {
// 往池中添加对象
addObject();
}
}
/**
* 获取连接
*
* @return
* @throws Exception
*/
@Override
public FTPClient borrowObject() throws Exception {
FTPClient client = ftpBlockingQueue.take();
if (ObjectUtils.isEmpty(client)) {
client = ftpClientFactory.create();
// 放入连接池
returnObject(client);
// 验证对象是否有效 这里通过实践验证 如果长时间不校验是否存活,则这里会报通道已断开等错误
} else if (!ftpClientFactory.validateObject(ftpClientFactory.wrap(client))) {
// 对无效的对象进行处理
invalidateObject(client);
// 创建新的对象
client = ftpClientFactory.create();
// 将新的对象放入连接池
returnObject(client);
}
return client;
}
@Override
public void returnObject(FTPClient client) {
try {
if (client != null && !ftpBlockingQueue.offer(client, 3, TimeUnit.SECONDS)) {
ftpClientFactory.destroyObject(ftpClientFactory.wrap(client));
}
} catch (InterruptedException e) {
log.error("return ftp client interrupted ...{}", e);
}
}
@Override
public void invalidateObject(FTPClient client) {
try {
if (client.isConnected()) {
client.logout();
}
} catch (IOException io) {
log.error("ftp client logout failed...{}", io);
} finally {
try {
client.disconnect();
} catch (IOException io) {
log.error("close ftp client failed...{}", io);
}
ftpBlockingQueue.remove(client);
}
}
/**
* 增加一个新的链接,超时失效
*/
@Override
public void addObject() throws Exception {
// 插入对象到队列
ftpBlockingQueue.offer(ftpClientFactory.create(), 3, TimeUnit.SECONDS);
}
/**
* 关闭连接池
*/
@Override
public void close() {
try {
while (ftpBlockingQueue.iterator().hasNext()) {
FTPClient client = ftpBlockingQueue.take();
ftpClientFactory.destroyObject(ftpClientFactory.wrap(client));
}
} catch (Exception e) {
log.error("close ftp client ftpBlockingQueue failed...{}", e);
}
}
public BlockingQueue<FTPClient> getFtpBlockingQueue() {
return ftpBlockingQueue;
}
}
4、基于Hutool ftp工具类源码实现接入ftp连接池抽象类
package com.faea.bus.core.ftp;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.util.List;
/**
* @author liuchao
* @date 2020/6/28
*/
public abstract class FaeaAbstractFtp {
/**
* 打开指定目录
*
* @param directory directory
* @return 是否打开目录
*/
public abstract boolean cd(String directory);
/**
* 打开上级目录
*
* @return 是否打开目录
* @since 4.0.5
*/
public boolean toParent() {
return cd("..");
}
/**
* 远程当前目录(工作目录)
*
* @return 远程当前目录
*/
public abstract String pwd();
/**
* 在当前远程目录(工作目录)下创建新的目录
*
* @param dir 目录名
* @return 是否创建成功
*/
public abstract boolean mkdir(String dir);
/**
* 文件或目录是否存在
*
* @param path 目录
* @return 是否存在
*/
public boolean exist(String path) {
final String fileName = FileUtil.getName(path);
final String dir = StrUtil.removeSuffix(path, fileName);
final List<String> names = ls(dir);
return containsIgnoreCase(names, fileName);
}
/**
* 遍历某个目录下所有文件和目录,不会递归遍历
*
* @param path 需要遍历的目录
* @return 文件和目录列表
*/
public abstract List<String> ls(String path);
/**
* 删除指定目录下的指定文件
*
* @param path 目录路径
* @return 是否存在
*/
public abstract boolean delFile(String path);
/**
* 删除文件夹及其文件夹下的所有文件
*
* @param dirPath 文件夹路径
* @return boolean 是否删除成功
*/
public abstract boolean delDir(String dirPath);
/**
* 创建指定文件夹及其父目录,从根目录开始创建,创建完成后回到默认的工作目录
*
* @param dir 文件夹路径,绝对路径
*/
public void mkDirs(String dir) {
final String[] dirs = StrUtil.trim(dir).split("[\\\\/]+");
final String now = pwd();
if (dirs.length > 0 && StrUtil.isEmpty(dirs[0])) {
//首位为空,表示以/开头
this.cd(StrUtil.SLASH);
}
for (int i = 0; i < dirs.length; i++) {
if (StrUtil.isNotEmpty(dirs[i])) {
if (false == cd(dirs[i])) {
//目录不存在时创建
mkdir(dirs[i]);
cd(dirs[i]);
}
}
}
// 切换回工作目录
cd(now);
}
/**
* 将本地文件上传到目标服务器,目标文件名为destPath,若destPath为目录,则目标文件名将与srcFilePath文件名相同。覆盖模式
*
* @param srcFilePath 本地文件路径
* @param destFile 目标文件
* @return 是否成功
*/
public abstract boolean upload(String srcFilePath, File destFile);
/**
* 下载文件
*
* @param path 文件路径
* @param outFile 输出文件或目录
*/
public abstract void download(String path, File outFile);
// ---------------------------------------------------------------------------------------------------------------------------------------- Private method start
/**
* 是否包含指定字符串,忽略大小写
*
* @param names 文件或目录名列表
* @param nameToFind 要查找的文件或目录名
* @return 是否包含
*/
private static boolean containsIgnoreCase(List<String> names, String nameToFind) {
if (CollUtil.isEmpty(names)) {
return false;
}
if (StrUtil.isEmpty(nameToFind)) {
return false;
}
for (String name : names) {
if (nameToFind.equalsIgnoreCase(name)) {
return true;
}
}
return false;
}
// ---------------------------------------------------------------------------------------------------------------------------------------- Private method end
/**
* 关闭连接
*
* @author liuchao
* @date 2020/6/28
*/
public abstract void close() throws Exception;
}
5、工具类抽象实现
package com.faea.bus.core.ftp;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.faea.bus.core.exception.FaeaBusinessException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Ftp 工具类实现
*
* @author liuchao
* @date 2020/6/28
*/
public class FaeaFtp extends FaeaAbstractFtp {
private FTPClient client;
private FtpClientPool ftpClientPool;
public FaeaFtp(FtpClientPool ftpClientPool, FTPClient client) {
this.ftpClientPool = ftpClientPool;
this.client = client;
}
public FaeaFtp(FtpClientPool ftpClientPool) throws Exception {
this.ftpClientPool = ftpClientPool;
this.client = ftpClientPool.borrowObject();
}
/**
* 执行完操作是否返回当前目录
*/
private boolean backToPwd;
/**
* 设置执行完操作是否返回当前目录
*
* @param backToPwd 执行完操作是否返回当前目录
* @return this
*/
public FaeaFtp setBackToPwd(boolean backToPwd) {
this.backToPwd = backToPwd;
return this;
}
/**
* 改变目录
*
* @param directory 目录
* @return 是否成功
*/
@Override
public boolean cd(String directory) {
if (StrUtil.isBlank(directory)) {
return false;
}
boolean flag;
try {
flag = client.changeWorkingDirectory(directory);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
return flag;
}
/**
* 远程当前目录
*
* @return 远程当前目录
*/
@Override
public String pwd() {
try {
return client.printWorkingDirectory();
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
}
@Override
public List<String> ls(String path) {
final FTPFile[] ftpFiles = lsFiles(path);
final List<String> fileNames = new ArrayList<>();
for (FTPFile ftpFile : ftpFiles) {
fileNames.add(ftpFile.getName());
}
return fileNames;
}
/**
* 遍历某个目录下所有文件和目录,不会递归遍历
*
* @param path 目录
* @return 文件或目录列表
*/
public FTPFile[] lsFiles(String path) {
String pwd = null;
if (StrUtil.isNotBlank(path)) {
pwd = pwd();
cd(path);
}
FTPFile[] ftpFiles;
try {
ftpFiles = this.client.listFiles();
} catch (IOException e) {
throw new FaeaBusinessException(e);
} finally {
// 回到原目录
cd(pwd);
}
return ftpFiles;
}
@Override
public boolean mkdir(String dir) {
boolean flag = true;
try {
flag = this.client.makeDirectory(dir);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
return flag;
}
/**
* 判断ftp服务器文件是否存在
*
* @param path 文件路径
* @return 是否存在
*/
public boolean existFile(String path) {
FTPFile[] ftpFileArr;
try {
ftpFileArr = client.listFiles(path);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
if (ArrayUtil.isNotEmpty(ftpFileArr)) {
return true;
}
return false;
}
@Override
public boolean delFile(String path) {
final String pwd = pwd();
final String fileName = FileUtil.getName(path);
final String dir = StrUtil.removeSuffix(path, fileName);
cd(dir);
boolean isSuccess;
try {
isSuccess = client.deleteFile(fileName);
} catch (IOException e) {
throw new FaeaBusinessException(e);
} finally {
// 回到原目录
cd(pwd);
}
return isSuccess;
}
@Override
public boolean delDir(String dirPath) {
FTPFile[] dirs;
try {
dirs = client.listFiles(dirPath);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
String name;
String childPath;
for (FTPFile ftpFile : dirs) {
name = ftpFile.getName();
childPath = StrUtil.format("{}/{}", dirPath, name);
if (ftpFile.isDirectory()) {
// 上级和本级目录除外
if (false == name.equals(".") && false == name.equals("..")) {
delDir(childPath);
}
} else {
delFile(childPath);
}
}
// 删除空目录
try {
return this.client.removeDirectory(dirPath);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
}
/**
* 上传文件到指定目录,可选:
*
* <pre>
* 1. path为null或""上传到当前路径
* 2. path为相对路径则相对于当前路径的子路径
* 3. path为绝对路径则上传到此路径
* </pre>
*
* @param path 服务端路径,可以为{@code null} 或者相对路径或绝对路径
* @param file 文件
* @return 是否上传成功
*/
@Override
public boolean upload(String path, File file) {
Assert.notNull(file, "file to upload is null !");
return upload(path, file.getName(), file);
}
/**
* 上传文件到指定目录,可选:
*
* <pre>
* 1. path为null或""上传到当前路径
* 2. path为相对路径则相对于当前路径的子路径
* 3. path为绝对路径则上传到此路径
* </pre>
*
* @param file 文件
* @param path 服务端路径,可以为{@code null} 或者相对路径或绝对路径
* @param fileName 自定义在服务端保存的文件名
* @return 是否上传成功
*/
public boolean upload(String path, String fileName, File file) {
try (InputStream in = FileUtil.getInputStream(file)) {
return upload(path, fileName, in);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
}
/**
* 上传文件到指定目录,可选:
*
* <pre>
* 1. path为null或""上传到当前路径
* 2. path为相对路径则相对于当前路径的子路径
* 3. path为绝对路径则上传到此路径
* </pre>
*
* @param path 服务端路径,可以为{@code null} 或者相对路径或绝对路径
* @param fileName 文件名
* @param fileStream 文件流
* @return 是否上传成功
*/
public boolean upload(String path, String fileName, InputStream fileStream) {
try {
client.setFileType(FTPClient.BINARY_FILE_TYPE);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
String pwd = null;
if (this.backToPwd) {
pwd = pwd();
}
if (StrUtil.isNotBlank(path)) {
mkDirs(path);
boolean isOk = cd(path);
if (false == isOk) {
return false;
}
}
try {
return client.storeFile(fileName, fileStream);
} catch (IOException e) {
throw new FaeaBusinessException(e);
} finally {
if (this.backToPwd) {
cd(pwd);
}
}
}
/**
* 下载文件
*
* @param path 文件路径
* @param outFile 输出文件或目录
*/
@Override
public void download(String path, File outFile) {
final String fileName = FileUtil.getName(path);
final String dir = StrUtil.removeSuffix(path, fileName);
download(dir, fileName, outFile);
}
/**
* 下载文件
*
* @param path 文件路径
* @param fileName 文件名
* @param outFile 输出文件或目录
*/
public void download(String path, String fileName, File outFile) {
if (outFile.isDirectory()) {
outFile = new File(outFile, fileName);
}
if (false == outFile.exists()) {
FileUtil.touch(outFile);
}
try (OutputStream out = FileUtil.getOutputStream(outFile)) {
download(path, fileName, out);
} catch (IOException e) {
throw new FaeaBusinessException(e);
}
}
/**
* 下载文件到输出流
*
* @param path 文件路径
* @param fileName 文件名
* @param out 输出位置
*/
public void download(String path, String fileName, OutputStream out) {
String pwd = null;
if (this.backToPwd) {
pwd = pwd();
}
cd(path);
try {
client.setFileType(FTPClient.BINARY_FILE_TYPE);
client.retrieveFile(fileName, out);
} catch (IOException e) {
throw new FaeaBusinessException(e);
} finally {
if (backToPwd) {
cd(pwd);
}
}
}
@Override
public void close() {
ftpClientPool.returnObject(client);
}
public void setClient(FTPClient client) {
this.client = client;
}
}
6、Ftp如果长时间不校验存活,再从连接池中获取到的client调用校验方法会报错,需要有一个心跳不断的校验是否存活,这里实现用了一个线程
package com.faea.bus.core.ftp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
/**
* 检测ftp客户端是否在活着
*
* @author liuchao
* @date 2020/6/30
*/
@Slf4j
public class FtpClientKeepAlive {
private KeepAliveThread keepAliveThread;
@Autowired
private FtpClientPool ftpClientPool;
private final String THREAD_NAME = "ftp-client-alive-thread";
@PostConstruct
public void init() {
// 启动心跳检测线程
if (keepAliveThread == null) {
keepAliveThread = new KeepAliveThread();
Thread thread = new Thread(keepAliveThread, THREAD_NAME);
thread.start();
}
}
class KeepAliveThread implements Runnable {
@Override
public void run() {
FTPClient ftpClient = null;
while (true) {
try {
BlockingQueue<FTPClient> pool = ftpClientPool.getFtpBlockingQueue();
if (pool != null && pool.size() > 0) {
Iterator<FTPClient> it = pool.iterator();
while (it.hasNext()) {
ftpClient = it.next();
boolean result = ftpClient.sendNoOp();
if (log.isDebugEnabled()) {
log.info("心跳结果: {}", result);
}
if (!result) {
ftpClientPool.invalidateObject(ftpClient);
}
}
}
} catch (Exception e) {
log.error("ftp心跳检测异常", e);
ftpClientPool.invalidateObject(ftpClient);
}
// 每30s发送一次心跳
try {
Thread.sleep(1000 * 30);
} catch (InterruptedException e) {
log.error("ftp休眠异常", e);
}
}
}
}
}
7、SpringBoot 自动化配置
package com.faea.bus.core.config;
import com.faea.bus.core.ftp.FtpClientFactory;
import com.faea.bus.core.ftp.FtpClientKeepAlive;
import com.faea.bus.core.ftp.FtpClientPool;
import com.faea.bus.core.properties.FtpProperties;
import com.faea.bus.core.utils.FaeaFtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* @author liuchao
* @date 2020/6/28
*/
@Slf4j
@EnableConfigurationProperties(FtpProperties.class)
public class FtpConfig {
@Autowired
FtpProperties ftpProperties;
/**
* 客户端工厂
*
* @return
*/
@Bean
public FtpClientFactory ftpClientFactory() {
return new FtpClientFactory(ftpProperties);
}
/**
* 连接池
*
* @param ftpClientFactory
* @return
* @throws Exception
*/
@Bean
public FtpClientPool ftpClientPool(FtpClientFactory ftpClientFactory) throws Exception {
return new FtpClientPool(ftpClientFactory);
}
/**
* ftp 工具类
*/
@Bean
@ConditionalOnMissingBean
public FaeaFtpUtil faeaFtpUtil() {
return new FaeaFtpUtil();
}
/**
* 检测ftp是否在活着
*/
@Bean
@ConditionalOnBean(FtpClientPool.class)
public FtpClientKeepAlive ftpClientKeepAlive() {
return new FtpClientKeepAlive();
}
}
8、在需要使用的项目中启用ftp,这里通过注解实现,在需要使用的应用的配置类中引入这个注解即可
package com.faea.bus.core.annotation;
import com.faea.bus.core.config.FtpConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 启用Ftp自动配置
*
* @author liuchao
* @date 2020/6/28
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(FtpConfig.class)
public @interface EnableFtp {
}
9、最后来一个使用的工具类
package com.faea.bus.core.utils;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.faea.bus.core.constants.FaeaBusConstant;
import com.faea.bus.core.exception.FaeaBusinessException;
import com.faea.bus.core.ftp.FaeaFtp;
import com.faea.bus.core.ftp.FtpClientPool;
import com.faea.core.collections.SynchronizedStack;
import com.faea.core.utils.FaeaStringPool;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* ftp 工具类
*
* @author liuchao
* @date 2020/6/28
*/
public class FaeaFtpUtil {
@Autowired
FtpClientPool ftpClientPool;
@Value("${spring.profiles.active}")
private String activeProfile;
private final MimeTypes allTypes = MimeTypes.getDefaultMimeTypes();
/**
* 缓存FaeaFtp 对象消除 FaeaFtp频繁创建和销毁丢失的性能
*/
private SynchronizedStack<FaeaFtp> faeaFtpCache;
public FaeaFtpUtil() {
this.faeaFtpCache = new SynchronizedStack<>();
}
/**
* 获取ftp
*
* @return com.faea.bus.core.ftp.FaeaFtp
* @author liuchao
* @date 2020/6/28
*/
private FaeaFtp getFtp() {
try {
FaeaFtp ftp = faeaFtpCache.pop();
FTPClient client = ftpClientPool.borrowObject();
if (ObjectUtil.isEmpty(ftp)) {
ftp = new FaeaFtp(ftpClientPool, client);
} else {
ftp.setClient(client);
}
//使用完需要还原到原来目录
ftp.setBackToPwd(Boolean.TRUE);
return ftp;
} catch (Exception e) {
throw new FaeaBusinessException("获取ftp连接失败", e);
}
}
/**
* 通过MultipartFile上传文件
*
* @param typeEnum 类型
* @param file 文件
* @return java.lang.String
* @author liuchao
* @date 2020/6/10
*/
public String upload(FaeaBusConstant.FtpUploadTypeEnum typeEnum, MultipartFile file) {
try {
String originalFileName = file.getOriginalFilename();
String fileExtendName = originalFileName.substring(originalFileName.lastIndexOf(FaeaStringPool.DOT));
return upload(typeEnum, file.getInputStream(), fileExtendName);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 文件上传
*
* @param typeEnum 上传类型
* @param is 流程
* @param fileExtendName 扩展名称 eg:'.jpg'
* @return java.lang.String
* @author liuchao
* @date 2020/6/28
*/
public String upload(FaeaBusConstant.FtpUploadTypeEnum typeEnum, InputStream is, String fileExtendName) {
FaeaFtp ftp = getFtp();
String path = typeEnum.getDir() + activeProfile + File.separator
+ DateUtil.date().toDateStr();
String fileName = IdUtil.fastSimpleUUID() + fileExtendName;
try {
return ftp.upload(path, fileName, is) ? path + File.separator + fileName : null;
} finally {
ftp.close();
faeaFtpCache.push(ftp);
IoUtil.close(is);
}
}
/**
* Ftp上传文件
*
* @param typeEnum 上传类型
* @param strUrl 文件网络路径
* @return java.lang.String
* @author liuchao
* @date 2020-03-27
*/
public String upload(FaeaBusConstant.FtpUploadTypeEnum typeEnum, String strUrl) {
HttpURLConnection connection = null;
try {
URL url = new URL(strUrl);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
return upload(typeEnum, is, getFileExtName(connection.getContentType()));
} catch (MimeTypeException e) {
throw new FaeaBusinessException("获取上传文件类型失败");
} catch (IOException e) {
throw new FaeaBusinessException("上传文件失败");
} finally {
if (ObjectUtil.isNotEmpty(connection)) {
connection.disconnect();
}
}
}
/**
* 获取流文件扩展名
*
* @param contentType
* @return java.lang.String
* @author liuchao
* @date 2020/6/28
*/
public String getFileExtName(String contentType) throws MimeTypeException {
return allTypes.forName(contentType).getExtension();
}
}
10、中间使用到的依赖
-
spring-boot
-
hutool
-
lombok
-
apache commons-pool2
- com.faea 包属于我当前项目,可以相应的更改哈,我有点懒
11、优化版本,经过长期生产实践,单独整理项目供大家下载使用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/144727.html