1、maven依赖
<!-- FTP上传依赖-->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<!-- 使用commons-pool 实现FTP连接池 -->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
2、application.yml的配置
## FTP 服务器配置
FTP:
## FTP 服务器的 ip
HOSTNAME: ******
## FTP 服务的端口号
PORT: ******
## FTP 服务器的登录用户账号
USERNAME: ******
## FTP 服务器的登录用户密码
PASSWORD: ******
## FTP 连接池的连接对象个数
DEFAULT_POOL_SIZE: 10
#连接超时(0表示一直连接)毫秒
CLIENT_TIME_OUT: 60000
#是否设置为被动模式(Linux下模式必须设置)
IS_ENTER_LOCAL_PASSIVE_MODE: true
3、FTP 工厂
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool.PoolableObjectFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@Slf4j
@SuppressWarnings("all")
public class FtpClientFactory implements PoolableObjectFactory<FTPClient> {
@Override
public FTPClient makeObject() throws Exception {
FtpConstant ftpConstant = new FtpConstant();
FTPClient ftpClient = new FTPClient();
ftpClient.setControlEncoding("utf-8");
ftpClient.setConnectTimeout(ftpConstant.getClientTimeOut());
try {
if (!ftpClient.isConnected()) {
ftpClient.connect(ftpConstant.getHostname(), ftpConstant.getPort());
}
ftpClient.login(ftpConstant.getUsername(), ftpConstant.getPassword());
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
log.info("连接失败ftp服务器:{}:{}", ftpConstant.getHostname(), ftpConstant.getPort());
}
ftpClient.enterLocalPassiveMode();
FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"));
ftpClient.setControlEncoding("UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
return ftpClient;
}
@Override
public void destroyObject(FTPClient ftpClient) throws Exception {
try {
if (ftpClient != null && ftpClient.isConnected()) {
log.info("通过发送QUIT命令退出FTP服务器");
ftpClient.logout();
}
} catch (Exception e) {
log.error("ftp Exception , 错误信息:{}", e.toString());
throw e;
} finally {
if (ftpClient != null) {
log.info("关闭到FTP服务器的连接,连接参数恢复到默认值");
ftpClient.disconnect();
}
}
}
@Override
public boolean validateObject(FTPClient ftpClient) {
try {
return ftpClient.sendNoOp();
} catch (Exception e) {
log.error("验证客户端失败: {}", e.toString());
}
return false;
}
@Override
public void activateObject(FTPClient ftpClient) throws Exception {
}
@Override
public void passivateObject(FTPClient ftpClient) throws Exception {
}
}
4、FTP 连接池
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
@Slf4j
public class FtpClientPool implements ObjectPool<FTPClient> {
public static FtpClientFactory ftpClientFactory = new FtpClientFactory();
public static BlockingQueue<FTPClient> blockingQueue;
public static FtpConstant ftpConstant = new FtpConstant();
static {
blockingQueue = new ArrayBlockingQueue<>(ftpConstant.getDefaultPoolSize());
FtpClientFactory factory = new FtpClientFactory();
int count = 0;
while (count < ftpConstant.getDefaultPoolSize()) {
try {
blockingQueue.offer(factory.makeObject());
} catch (Exception e) {
log.error("初始化ftpClientPool 时 FtpFactory的makeObject()错误:", e);
}
count++;
}
log.info("ftpPool 中连接对象个数为:{}", blockingQueue.size());
}
@Override
public FTPClient borrowObject() throws Exception {
log.info("ftpPool 取出连接前的个数为:" + getNumIdle());
FTPClient client = blockingQueue.poll(1, TimeUnit.MINUTES);
log.info("ftpPool 取出连接后的个数为:" + getNumIdle());
if (client == null) {
this.addObject();
log.info("client==null ");
client = borrowObject();
} else if (!ftpClientFactory.validateObject(client)) {
log.info("获取的连接对象无效");
try {
ftpClientFactory.destroyObject(client);
} catch (Exception e) {
log.info("invalidateObject error:{}", e.toString());
}
log.info("添加新的连接对象到 ftpClientPool 中");
this.addObject();
client = borrowObject();
}
return client;
}
@Override
public void returnObject(FTPClient ftpClient) throws Exception {
log.info("回收连接对象前的ftpClientPool池中连接数为:" + getNumIdle());
if ((ftpClient != null)) {
if (!blockingQueue.offer(ftpClient)) {
try {
ftpClientFactory.destroyObject(ftpClient);
log.info("销毁无效连接对象");
} catch (Exception e) {
throw e;
}
} else {
log.info("回收连接对象后的ftpClientPool池中连接数为:" + getNumIdle());
}
}
}
@Override
public void invalidateObject(FTPClient ftpClient) throws Exception {
blockingQueue.remove(ftpClient);
}
@Override
public void addObject() throws Exception, IllegalStateException, UnsupportedOperationException {
blockingQueue.offer(ftpClientFactory.makeObject(), ftpConstant.getClientTimeOut(), TimeUnit.MINUTES);
}
@Override
public int getNumIdle() throws UnsupportedOperationException {
return blockingQueue.size();
}
@Override
public int getNumActive() throws UnsupportedOperationException {
return ftpConstant.getDefaultPoolSize() - getNumIdle();
}
@Override
public void clear() throws Exception, UnsupportedOperationException {
}
@Override
public void close() throws Exception {
try {
while (blockingQueue.iterator().hasNext()) {
FTPClient client = blockingQueue.take();
ftpClientFactory.destroyObject(client);
}
} catch (Exception e) {
log.error("关闭连接池错误:{}", e.toString());
}
}
@Override
public void setFactory(PoolableObjectFactory<FTPClient> poolAbleObjectFactory) throws IllegalStateException, UnsupportedOperationException {
}
}
5、FTP 文件上传常量
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
@Data
public class FtpConstant {
private static String hostname;
private static Integer port;
private static String username;
private static String password;
private static Integer defaultPoolSize;
private static Integer clientTimeOut;
private static Boolean isEnterLocalPassiveMode;
@Value("${FTP.HOSTNAME}")
public void setHostname(String hostname) {
FtpConstant.hostname = hostname;
}
@Value("${FTP.PORT}")
public void setPort(Integer port) {
FtpConstant.port = port;
}
@Value("${FTP.USERNAME}")
public void setUsername(String username) {
FtpConstant.username = username;
}
@Value("${FTP.PASSWORD}")
public void setPassword(String password) {
FtpConstant.password = password;
}
@Value("${FTP.DEFAULT_POOL_SIZE}")
public void setDefaultPoolSize(Integer defaultPoolSize) {
FtpConstant.defaultPoolSize = defaultPoolSize;
}
@Value("${FTP.CLIENT_TIME_OUT}")
public void setClientTimeOut(Integer clientTimeOut) {
FtpConstant.clientTimeOut = clientTimeOut;
}
@Value("${FTP.IS_ENTER_LOCAL_PASSIVE_MODE}")
public void setIsEnterLocalPassiveMode(Boolean isEnterLocalPassiveMode) {
FtpConstant.isEnterLocalPassiveMode = isEnterLocalPassiveMode;
}
public String getHostname() {
return hostname;
}
public Integer getPort() {
return port;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public Integer getDefaultPoolSize() {
return defaultPoolSize;
}
public Integer getClientTimeOut() {
return clientTimeOut;
}
public Boolean getIsEnterLocalPassiveMode() {
return isEnterLocalPassiveMode;
}
}
6、FTP 具体上传下载工具类
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.charset.StandardCharsets;
@Slf4j
public class FtpTemplateUtils {
public static FtpClientPool ftpClientPool;
static {
ftpClientPool = new FtpClientPool();
}
public static int checkFileType(String filePath, String suffix, MultipartFile multipartFile) throws Exception {
try (
InputStream inputStream = multipartFile.getInputStream()) {
boolean status;
String fileName = multipartFile.getOriginalFilename();
String fileNameSuffix = null;
if (fileName != null) {
fileNameSuffix = fileName.substring(fileName.lastIndexOf(".") + 1);
}
boolean checkSuffix = true;
if (fileNameSuffix != null && !suffix.contains(fileNameSuffix)) {
checkSuffix = false;
}
if (checkSuffix) {
status = uploadFile(filePath, fileName, inputStream);
} else {
return -1;
}
if (status) {
return 1;
}
} catch (IOException e) {
e.printStackTrace();
return -2;
}
return -3;
}
public static boolean uploadFile(String filePath, String fileName, InputStream inputStream) throws Exception {
FTPClient ftpClient = ftpClientPool.borrowObject();
try {
log.info("开始上传文件");
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
createDirecroty(ftpClient, filePath);
ftpClient.makeDirectory(filePath);
ftpClient.changeWorkingDirectory(filePath);
boolean b = ftpClient.storeFile(fileName, inputStream);
if (b) {
log.info("上传文件成功");
} else {
log.info("上传文件失败");
}
return b;
} catch (Exception e) {
log.info("上传文件失败");
e.printStackTrace();
return false;
} finally {
ftpClientPool.returnObject(ftpClient);
}
}
public static Integer deleteFile(String path, String fileName) throws Exception {
FTPClient ftpClient = ftpClientPool.borrowObject();
try {
log.info("开始删除文件");
if (existFile(ftpClient, path + "/" + fileName)) {
ftpClient.changeWorkingDirectory(path);
boolean b = ftpClient.deleteFile(fileName);
if (b) {
log.info("文件删除成功");
return 1;
} else {
log.info("文件删除失败");
return -1;
}
} else {
log.info("文件已被删除");
return -2;
}
} catch (Exception e) {
log.info("删除文件失败");
e.printStackTrace();
} finally {
ftpClientPool.returnObject(ftpClient);
}
return -1;
}
public static boolean removeDirectory(String filePath, String pathName) throws Exception {
FTPClient ftpClient = ftpClientPool.borrowObject();
try {
log.info("开始删除文件");
ftpClient.changeWorkingDirectory(filePath);
removeDirectoryALLFile(ftpClient, filePath, pathName);
log.info("删除文件成功");
return true;
} catch (Exception e) {
log.error("删除文件失败{}", e.toString());
return false;
} finally {
ftpClientPool.returnObject(ftpClient);
}
}
public static InputStream downFileToInputStream(String remotePath, String fileName) throws Exception {
FTPClient ftpClient = ftpClientPool.borrowObject();
try {
ftpClient.changeWorkingDirectory(remotePath);
FTPFile[] ftpFiles = ftpClient.listFiles();
for (FTPFile ftpFile : ftpFiles) {
if (ftpFile.getName().equals(fileName)) {
InputStream inputStream = ftpClient.retrieveFileStream(ftpFile.getName());
if (inputStream != null) {
return inputStream;
}
}
}
} catch (IOException e) {
log.error("下载文件失败{}", e.toString());
} finally {
ftpClientPool.returnObject(ftpClient);
}
return null;
}
public static File downloadFile(String filePath, String fileName, String localPath) throws Exception {
FTPClient ftpClient = ftpClientPool.borrowObject();
try {
File localFile = null;
ftpClient.changeWorkingDirectory(filePath);
FTPFile[] ftpFiles = ftpClient.listFiles();
for (FTPFile file : ftpFiles) {
if (fileName.equalsIgnoreCase(file.getName())) {
log.info("开始下载");
File file2 = new File(localPath);
if (!file2.exists()) {
file2.mkdirs();
}
localFile = new File(localPath + "/" + file.getName());
try (OutputStream os = new FileOutputStream(localFile);) {
ftpClient.retrieveFile(file.getName(), os);
log.info("下载成功,文件路径:" + localPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return localFile;
} catch (IOException e) {
log.error("下载文件失败{}", e.toString());
} finally {
ftpClientPool.returnObject(ftpClient);
}
return null;
}
public static boolean determineFileExist(String fileName, String filePath) throws Exception {
FTPClient ftpClient = ftpClientPool.borrowObject();
return existFile(ftpClient, filePath + "/" + fileName);
}
private static void removeDirectoryALLFile(FTPClient ftpClient, String path, String fileName) {
try {
FTPFile[] files = ftpClient.listFiles(fileName);
if (null != files && files.length > 0) {
for (FTPFile file : files) {
String newpath = path + "/" + fileName;
String newfileName = fileName + "/" + file.getName();
if (file.isDirectory()) {
ftpClient.changeWorkingDirectory(newpath);
removeDirectoryALLFile(ftpClient, newpath, newfileName);
} else {
ftpClient.deleteFile(newfileName);
}
}
}
ftpClient.changeWorkingDirectory(path);
ftpClient.removeDirectory(fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
private static boolean makeDirectory(FTPClient ftpClient, String dir) {
try {
boolean flag = ftpClient.makeDirectory(dir);
if (flag) {
log.info("创建文件夹" + dir + " 成功!");
return true;
} else {
log.info("创建文件夹" + dir + " 失败!");
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private static boolean changeWorkingDirectory(FTPClient ftpClient, String directory) {
try {
boolean flag = ftpClient.changeWorkingDirectory(directory);
if (flag) {
log.info("进入文件夹" + directory + " 成功!");
return true;
} else {
log.info("进入文件夹" + directory + " 失败!开始创建文件夹");
return false;
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
return false;
}
private static void createDirecroty(FTPClient ftpClient, String remote) throws IOException {
String directory = remote + "/";
if (!"/".equalsIgnoreCase(directory) && !changeWorkingDirectory(ftpClient, directory)) {
int start;
int end;
if (directory.startsWith("/")) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf("/", start);
String path = "";
do {
String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), StandardCharsets.ISO_8859_1);
path = path + "/" + subDirectory;
if (!existFile(ftpClient, path)) {
if (!makeDirectory(ftpClient, subDirectory)) {
log.info("创建目录[" + subDirectory + "]失败");
}
}
changeWorkingDirectory(ftpClient, subDirectory);
start = end + 1;
end = directory.indexOf("/", start);
} while (end > start);
}
}
private static boolean existFile(FTPClient ftpClient, String filePath) {
boolean flag = false;
FTPFile[] ftpFileArr = new FTPFile[0];
try {
ftpFileArr = ftpClient.listFiles(filePath);
} catch (IOException e) {
e.printStackTrace();
}
if (ftpFileArr.length > 0) {
flag = true;
}
return flag;
}
}
7、测试类
import com.example.ftppool.util.FtpTemplateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
@RestController
@RequestMapping("/ftp")
@Slf4j
public class FtpFileController {
@RequestMapping(value = "/downloadFile", method = RequestMethod.GET)
public String downloadFile(String fileName) throws Exception {
String remotePath = "/develop/deploy/front/hainan-gis/tmp";
String localPath = "C:\\";
File file1 = FtpTemplateUtils.downloadFile(remotePath, fileName, localPath);
return file1.getName();
}
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public Integer uploadFile(MultipartFile file) throws Exception {
String remotePath = "/ftpFile/data/hainan/report";
String suffix = "doc/DOC/docx/DOCX/pdf/PDF/xlsx/xls/";
return FtpTemplateUtils.checkFileType(remotePath,suffix, file);
}
}