springboot快速啟動外掛ftp篇-連線池
阿新 • • 發佈:2018-12-10
功能點:
- 初始化ftp連線池
- 封裝ftp基礎操作
1.建立maven工程
略
2.編寫pom檔案
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>iotstarter</artifactId> <groupId>com.iot</groupId> <version>0.0.1-RELEASES</version> </parent> <modelVersion>4.0.0</modelVersion> <packaging>jar</packaging> <groupId>com.iot</groupId> <artifactId>iot-ftp-spring-boot-starter</artifactId> <version>0.0.1-RELEASES</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> </dependencies> </project>
3.編寫配置實體(讀取配置檔案資訊)
package com.iot.ftp.properties; import lombok.Data; import org.apache.commons.net.ftp.FTPClient; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "iot-ftp") @Data public class FtpOptionProperties { private String host; private int port= FTPClient.DEFAULT_PORT; private String username; private String password; private int bufferSize = 8096; /** * 初始化連線數 */ private Integer initialSize = 0; private String encoding; }
4.編寫總配置類(初始化單例bean)
package com.iot.ftp.config; import com.iot.ftp.properties.FtpOptionProperties; import com.iot.ftp.service.IotFtpService; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.pool2.ObjectPool; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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 javax.annotation.PreDestroy; /** * 描述: els快捷操作配置 * @author: myx * @date: 2018/5/3 * Copyright © 2018-hotpot. All rights reserved. */ @Configuration//開啟配置 @EnableConfigurationProperties(FtpOptionProperties.class)//開啟使用對映實體物件 @ConditionalOnClass({IotFtpService.class,GenericObjectPool.class, FTPClient.class})//存在IotFtpService時初始化該配置類 @ConditionalOnProperty//存在對應配置資訊時初始化該配置類 ( prefix = "iot-ftp",//存在配置字首hello name = "isopen", havingValue = "true",//開啟 matchIfMissing = true//缺失檢查 ) public class FtpConfiguration { private Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired private FtpOptionProperties ftpOptionProperties; private ObjectPool<FTPClient> pool; /** * 預先載入FTPClient連線到物件池中 * @param initialSize 初始化連線數 * @param maxIdle 最大空閒連線數 */ private void preLoadingFtpClient(Integer initialSize, int maxIdle) { if (initialSize == null || initialSize <= 0) { return; } int size = Math.min(initialSize.intValue(), maxIdle); for (int i = 0; i < size; i++) { try { pool.addObject(); } catch (Exception e) { log.error("preLoadingFtpClient error...", e); } } } @PreDestroy public void destroy() { if (pool != null) { pool.close(); log.info("銷燬ftpClientPool..."); } } /** * 根據條件判斷不存在ElsService時初始化新bean到SpringIoc * @return */ @Bean//建立HelloService實體bean @ConditionalOnMissingBean(IotFtpService.class)//缺失HelloService實體bean時,初始化HelloService並新增到SpringIoc public IotFtpService iotFtpService() throws Exception{ log.info(">>>The IotFtpService Not Found,Execute Creat New Bean."); GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setTestOnBorrow(true); poolConfig.setTestOnReturn(true); poolConfig.setTestWhileIdle(true); poolConfig.setMinEvictableIdleTimeMillis(60000); poolConfig.setSoftMinEvictableIdleTimeMillis(50000); poolConfig.setTimeBetweenEvictionRunsMillis(30000); pool = new GenericObjectPool<>(new FtpClientPooledObjectFactory(ftpOptionProperties), poolConfig); preLoadingFtpClient(ftpOptionProperties.getInitialSize(), poolConfig.getMaxIdle()); IotFtpService iotFtpService =new IotFtpService(); iotFtpService.setFtpClientPool(pool); iotFtpService.setHasInit(true); return iotFtpService; } /** * FtpClient物件工廠類 */ static class FtpClientPooledObjectFactory implements PooledObjectFactory<FTPClient> { private Logger log = LoggerFactory.getLogger(this.getClass()); private FtpOptionProperties props; public FtpClientPooledObjectFactory(FtpOptionProperties props) { this.props = props; } @Override public PooledObject<FTPClient> makeObject() throws Exception { FTPClient ftpClient = new FTPClient(); try { ftpClient.connect(props.getHost(), props.getPort()); ftpClient.login(props.getUsername(), props.getPassword()); log.info("連線FTP伺服器返回碼{}", ftpClient.getReplyCode()); ftpClient.setBufferSize(props.getBufferSize()); ftpClient.setControlEncoding(props.getEncoding()); ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); ftpClient.enterLocalPassiveMode(); return new DefaultPooledObject<>(ftpClient); } catch (Exception e) { log.error("建立FTP連線失敗", e); if (ftpClient.isAvailable()) { ftpClient.disconnect(); } ftpClient = null; throw new Exception("建立FTP連線失敗", e); } } @Override public void destroyObject(PooledObject<FTPClient> p) throws Exception { FTPClient ftpClient = getObject(p); if (ftpClient != null && ftpClient.isConnected()) { ftpClient.disconnect(); } } @Override public boolean validateObject(PooledObject<FTPClient> p) { FTPClient ftpClient = getObject(p); if (ftpClient == null || !ftpClient.isConnected()) { return false; } try { ftpClient.changeWorkingDirectory("/"); return true; } catch (Exception e) { log.error("驗證FTP連線失敗::{}",e.getMessage()); return false; } } @Override public void activateObject(PooledObject<FTPClient> p) throws Exception { } @Override public void passivateObject(PooledObject<FTPClient> p) throws Exception { } private FTPClient getObject(PooledObject<FTPClient> p) { if (p == null || p.getObject() == null) { return null; } return p.getObject(); } } }
5.編寫ftp操作類(核心)
package com.iot.ftp.service;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.pool2.ObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 描述:ftp服務端
* @author: myx
* @date: 2018/5/3
* Copyright © 2018-hotpot. All rights reserved.
*/
@Data
public class IotFtpService {
private Logger log = LoggerFactory.getLogger(this.getClass());
/**
* ftpClient連線池初始化標誌
*/
private boolean hasInit = false;
/**
* ftpClient連線池
*/
private ObjectPool<FTPClient> ftpClientPool;
/**
* 上傳檔案
* @param pathname ftp服務儲存地址
* @param fileName 上傳到ftp的檔名
* @param originfilename 待上傳檔案的名稱(絕對地址) *
* @return
*/
public boolean uploadFile( String pathname, String fileName,String originfilename){
boolean flag = false;
InputStream inputStream = null;
FTPClient ftpClient = getFtpClient();
try{
log.info("開始上傳檔案");
inputStream = new FileInputStream(new File(originfilename));
ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
CreateDirecroty(pathname,ftpClient);
ftpClient.makeDirectory(pathname);
ftpClient.changeWorkingDirectory(pathname);
ftpClient.storeFile(fileName, inputStream);
inputStream.close();
flag = true;
log.info("上傳檔案成功");
}catch (Exception e) {
log.error("上傳檔案失敗");
e.printStackTrace();
}finally{
releaseFtpClient(ftpClient);
}
return flag;
}
/**
* 上傳檔案
* @param pathname ftp服務儲存地址
* @param fileName 上傳到ftp的檔名
* @param inputStream 輸入檔案流
* @return
*/
public boolean uploadFile( String pathname, String fileName,InputStream inputStream){
boolean flag = false;
FTPClient ftpClient = getFtpClient();
try{
log.info("開始上傳檔案");
ftpClient.setFileType(ftpClient.BINARY_FILE_TYPE);
CreateDirecroty(pathname,ftpClient);
ftpClient.makeDirectory(pathname);
ftpClient.changeWorkingDirectory(pathname);
ftpClient.storeFile(fileName, inputStream);
inputStream.close();
flag = true;
log.info("上傳檔案成功");
}catch (Exception e) {
log.error("上傳檔案失敗");
e.printStackTrace();
}finally{
releaseFtpClient(ftpClient);
}
return flag;
}
/** * 下載檔案 *
* @param pathname FTP伺服器檔案目錄 *
* @param filename 檔名稱 *
* @param localpath 下載後的檔案路徑 *
* @return */
public boolean downloadFile(String pathname, String filename, String localpath){
boolean flag = false;
OutputStream os=null;
FTPClient ftpClient = getFtpClient();
try {
log.info("開始下載檔案");
//切換FTP目錄
ftpClient.changeWorkingDirectory(pathname);
FTPFile[] ftpFiles = ftpClient.listFiles();
for(FTPFile file : ftpFiles){
if(filename.equalsIgnoreCase(file.getName())){
File localFile = new File(localpath + "/" + file.getName());
os = new FileOutputStream(localFile);
ftpClient.retrieveFile(file.getName(), os);
os.close();
}
}
flag = true;
log.info("下載檔案成功");
} catch (Exception e) {
log.error("下載檔案失敗");
e.printStackTrace();
} finally{
releaseFtpClient(ftpClient);
if(null != os){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return flag;
}
/** * 刪除檔案 *
* @param pathname FTP伺服器儲存目錄 *
* @param filename 要刪除的檔名稱 *
* @return */
public boolean deleteFile(String pathname, String filename){
boolean flag = false;
FTPClient ftpClient = getFtpClient();
try {
log.info("開始刪除檔案");
//切換FTP目錄
ftpClient.changeWorkingDirectory(pathname);
ftpClient.dele(filename);
ftpClient.logout();
flag = true;
log.info("刪除檔案成功");
} catch (Exception e) {
log.error("刪除檔案失敗");
e.printStackTrace();
} finally {
releaseFtpClient(ftpClient);
}
return flag;
}
/**
* 按行讀取FTP檔案
*
* @param remoteFilePath 檔案路徑(path+fileName)
* @return
* @throws IOException
*/
public List<String> readFileByLine(String remoteFilePath) throws IOException {
FTPClient ftpClient = getFtpClient();
try (InputStream in = ftpClient.retrieveFileStream(encodingPath(remoteFilePath));
BufferedReader br = new BufferedReader(new InputStreamReader(in))) {
return br.lines().map(line -> StrUtil.trimToEmpty(line))
.filter(line -> StrUtil.isNotEmpty(line)).collect(Collectors.toList());
} finally {
ftpClient.completePendingCommand();
releaseFtpClient(ftpClient);
}
}
/**
* 獲取指定路徑下FTP檔案
*
* @param remotePath 路徑
* @return FTPFile陣列
* @throws IOException
*/
public FTPFile[] retrieveFTPFiles(String remotePath) throws IOException {
FTPClient ftpClient = getFtpClient();
try {
return ftpClient.listFiles(encodingPath(remotePath + "/"),
file -> file != null && file.getSize() > 0);
} finally {
releaseFtpClient(ftpClient);
}
}
/**
* 獲取指定路徑下FTP檔名稱
*
* @param remotePath 路徑
* @return ftp檔名稱列表
* @throws IOException
*/
public List<String> retrieveFileNames(String remotePath) throws IOException {
FTPFile[] ftpFiles = retrieveFTPFiles(remotePath);
if (ArrayUtil.isEmpty(ftpFiles)) {
return new ArrayList<>();
}
return Arrays.stream(ftpFiles).filter(Objects::nonNull)
.map(FTPFile::getName).collect(Collectors.toList());
}
/**
* 編碼檔案路徑
*/
private static String encodingPath(String path) throws UnsupportedEncodingException {
// FTP協議裡面,規定檔名編碼為iso-8859-1,所以目錄名或檔名需要轉碼
return new String(path.replaceAll("//", "/").getBytes("GBK"), "iso-8859-1");
}
/**
* 獲取ftpClient
*
* @return
*/
private FTPClient getFtpClient() {
checkFtpClientPoolAvailable();
FTPClient ftpClient = null;
Exception ex = null;
// 獲取連線最多嘗試3次
for (int i = 0; i < 3; i++) {
try {
ftpClient = ftpClientPool.borrowObject();
ftpClient.enterLocalPassiveMode();//被動模式
ftpClient.changeWorkingDirectory("/");
break;
} catch (Exception e) {
ex = e;
}
}
if (ftpClient == null) {
throw new RuntimeException("Could not get a ftpClient from the pool", ex);
}
return ftpClient;
}
/**
* 釋放ftpClient
*/
private void releaseFtpClient(FTPClient ftpClient) {
if (ftpClient == null) {
return;
}
try {
ftpClientPool.returnObject(ftpClient);
} catch (Exception e) {
log.error("Could not return the ftpClient to the pool", e);
// destoryFtpClient
if (ftpClient.isAvailable()) {
try {
ftpClient.disconnect();
} catch (IOException io) {
}
}
}
}
/**
* 檢查ftpClientPool是否可用
*/
private void checkFtpClientPoolAvailable() {
Assert.state(hasInit, "FTP未啟用或連線失敗!");
}
/**
* 建立多層目錄檔案,如果有ftp伺服器已存在該檔案,則不建立,如果無,則建立
* @param remote
* @param ftpClient
* @return
* @throws IOException
*/
public boolean CreateDirecroty(String remote,FTPClient ftpClient) throws IOException {
boolean success = true;
String directory = remote + "/";
// 如果遠端目錄不存在,則遞迴建立遠端伺服器目錄
if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(new String(directory),ftpClient)) {
int start = 0;
int end = 0;
if (directory.startsWith("/")) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf("/", start);
String path = "";
String paths = "";
while (true) {
String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "iso-8859-1");
path = path + "/" + subDirectory;
if (!existFile(path,ftpClient)) {
if (makeDirectory(subDirectory,ftpClient)) {
changeWorkingDirectory(subDirectory,ftpClient);
} else {
System.out.println("建立目錄[" + subDirectory + "]失敗");
changeWorkingDirectory(subDirectory,ftpClient);
}
} else {
changeWorkingDirectory(subDirectory,ftpClient);
}
paths = paths + "/" + subDirectory;
start = end + 1;
end = directory.indexOf("/", start);
// 檢查所有目錄是否建立完畢
if (end <= start) {
break;
}
}
}
return success;
}
/**
* 改變目錄路徑
* @param directory
* @param ftpClient
* @return
*/
public boolean changeWorkingDirectory(String directory,FTPClient ftpClient) {
boolean flag = true;
try {
flag = ftpClient.changeWorkingDirectory(directory);
if (flag) {
System.out.println("進入資料夾" + directory + " 成功!");
} else {
System.out.println("進入資料夾" + directory + " 失敗!開始建立資料夾");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
return flag;
}
//判斷ftp伺服器檔案是否存在
public boolean existFile(String path,FTPClient ftpClient) throws IOException {
boolean flag = false;
FTPFile[] ftpFileArr = ftpClient.listFiles(path);
if (ftpFileArr.length > 0) {
flag = true;
}
return flag;
}
//建立目錄
public boolean makeDirectory(String dir,FTPClient ftpClient) {
boolean flag = true;
try {
flag = ftpClient.makeDirectory(dir);
if (flag) {
log.info("建立資料夾" + dir + " 成功!");
} else {
log.info("建立資料夾" + dir + " 失敗!");
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
}
6.在resource/WEB-INF建立spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.iot.ftp.config.FtpConfiguration
7.使用說明
7.1 包引入
<dependency>
<groupId>com.iot</groupId>
<artifactId>iot-ftp-spring-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
7.2 配置檔案配置資訊
iot-ftp:
isopen: true #是否開啟
host: 192.168.2.1
port: 21
username: user
password: pwd
encoding: UTF-8
7.3 使用說明
@Autowired
private IotFtpService iotFtpService;
@GetMapping("/ftp")
public Object ftp() throws Exception{
for (int i = 0; i < 10; i++) {
boolean b = iotFtpService.uploadFile("ftp/testganinfo", "logstash-5.6.9.tar.gz", "f://logstash-5.6.9.tar.gz");
System.out.println("上傳檔案==>"+b);
boolean b1 = iotFtpService.downloadFile("ftp/testganinfo", "logstash-5.6.9.tar.gz", "c://");
System.out.println("下載檔案==>"+b1);
}
return iotFtpService.retrieveFileNames("ftp/testganinfo");
}