SpringBoot集成FTP上傳文件功能實現(xiàn)

FTP是什么?
文件傳輸協(xié)議(英語:File Transfer Protocol,縮寫:FTP)是一個用于在計算機網(wǎng)絡(luò)上在客戶端和服務(wù)器之間進行文件傳輸?shù)膽?yīng)用層協(xié)議。
SFTP 和 FTP 的區(qū)別
SFTP 和 FTP 是兩種不同的文件傳輸協(xié)議,它們在安全性、連接方式和功能上有顯著差異。
FTP(File Transfer Protocol)
- 使用明文傳輸數(shù)據(jù),包括用戶名和密碼。
- 需要兩個端口:控制端口(21)和數(shù)據(jù)端口(20)。
- 不支持加密,容易被中間人攻擊。
- 支持匿名登錄和主動/被動模式。
SFTP(SSH File Transfer Protocol)
- 基于 SSH 協(xié)議,所有數(shù)據(jù)傳輸均加密。
- 僅使用單一端口(默認(rèn) 22),簡化防火墻配置。
- 支持文件操作(如重命名、刪除)和目錄列表。
- 無需額外配置,依賴 SSH 服務(wù)即可使用。
SpringBoot集成FTP上傳文件
pom依賴
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>配置文件
ftp: host: host.test port: 21 username: ftptest01 password: xxxxxx path: /var/ftp/test/ outlink: https://host.test filePath: /test/ encoding: utf-8 maxActive: 100 minIdel: 2 maxIdel: 5 maxWaitMillis: 3000 passivemode: true
配置類
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author qf
* @version 1.0
* @data 2025/7/20 18:28
*/
@Data
@Component
@ConfigurationProperties(prefix = "ftp")
public class FTPConfig {
private String host;
private Integer port;
private String username;
private String password;
private String encoding;
private Integer maxActive;
private Integer minIdel;
private Integer maxIdel;
private Integer maxWaitMillis;
private Boolean passivemode;
private String outlink;
private String filePath;
private String path;
}對象池的創(chuàng)建與管理
import lombok.extern.log4j.Log4j2;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @author qf
* @version 1.0
* @data 2025/7/20 18:34
*/
@Log4j2
@Component
public class FtpClientFactory implements PooledObjectFactory<FTPClient> {
@Autowired
private FTPConfig ftpConfig;
/**
* 創(chuàng)建連接放入池中
*
* @return
* @throws Exception
*/
@Override
public PooledObject<FTPClient> makeObject() throws Exception {
FTPClient ftpClient = new FTPClient();
//ftpClient.setControlEncoding(ftpConfig.getEncoding());
ftpClient.setConnectTimeout(ftpConfig.getMaxWaitMillis());
try {
ftpClient.connect(ftpConfig.getHost(), ftpConfig.getPort());
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftpClient.disconnect();
log.warn("FTP服務(wù)拒絕鏈接");
return null;
}
boolean login = ftpClient.login(ftpConfig.getUsername(), ftpConfig.getPassword());
if (!login) {
log.warn("ftpClient登入失敗,username:{" + ftpConfig.getUsername() + "},password:{" + ftpConfig.getPassword() + "}");
throw new RuntimeException("ftpClient登入失敗,username:{" + ftpConfig.getUsername() + "},password:{" + ftpConfig.getPassword() + "}");
}
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ftpClient.setBufferSize(1024);
if (ftpConfig.getPassivemode()) {
ftpClient.enterLocalPassiveMode();
}
} catch (IOException e) {
e.printStackTrace();
}
log.info("添加一個FtpClient進入連接池");
return new DefaultPooledObject<>(ftpClient);
}
@Override
public void destroyObject(PooledObject<FTPClient> pooledObject) throws Exception {
FTPClient ftpClient = pooledObject.getObject();
try {
if (ftpClient != null && ftpClient.isConnected()) {
ftpClient.logout();
}
} catch (IOException e) {
throw new RuntimeException("沒有獲取到FtpClient或已斷開連接:", e);
} finally {
try {
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 連接狀態(tài)檢查
*
* @param pooledObject
* @return
*/
@Override
public boolean validateObject(PooledObject<FTPClient> pooledObject) {
FTPClient ftpClient = pooledObject.getObject();
try {
return ftpClient.sendNoOp();
} catch (IOException e) {
log.info("無法驗證FtpClient {}", e.getMessage());
return false;
}
}
@Override
public void activateObject(PooledObject<FTPClient> pooledObject) throws Exception {
}
@Override
public void passivateObject(PooledObject<FTPClient> pooledObject) throws Exception {
}
public FTPConfig getConfig() {
return this.ftpConfig;
}
}PooledObjectFactory 是 Apache Commons Pool 2 庫中的一個核心接口,它定義了用于創(chuàng)建和管理池化對象(即那些可以被多個客戶端重復(fù)使用的對象)生命周期的方法。通過實現(xiàn)這個接口,可以定制化對象的創(chuàng)建、激活、驗證和銷毀等過程,從而更好地控制對象池的行為。
其中:
- PooledObject makeObject() throws Exception
- 創(chuàng)建一個新的實例,并將其包裝在一個 PooledObject 實例中返回。這是用來生成新的池對象的方法。
- void destroyObject(PooledObject p) throws Exception
- 銷毀指定的池對象。當(dāng)對象池決定不再需要某個對象時,會調(diào)用此方法來清理資源。
- boolean validateObject(PooledObject p)
- 驗證池中的某個對象是否仍然有效。如果返回 true,則認(rèn)為該對象可以繼續(xù)使用;否則,可能需要重新創(chuàng)建或銷毀該對象。
- void activateObject(PooledObject p) throws Exception
- 當(dāng)從池中借用對象之前調(diào)用,用于“激活”對象,比如重置狀態(tài)等操作。
- void passivateObject(PooledObject p) throws Exception
當(dāng)對象歸還到池后調(diào)用,用于將對象置于“鈍化”狀態(tài),通常涉及清理工作以準(zhǔn)備下次使用。
FTP連接池初始化
import lombok.extern.log4j.Log4j2;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @author qf
* @version 1.0
* @data 2025/7/20 18:47
*/
@Lazy
@Log4j2
@Component
public class FtpClientPool {
private FtpClientFactory factory;
private final GenericObjectPool<FTPClient> internalPool;
/**
* 初始化連接池
*/
public FtpClientPool(@Autowired FtpClientFactory factory) {
log.info("**********************初始化Ftp連接池*********************");
this.factory = factory;
FTPConfig config = factory.getConfig();
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(config.getMaxActive());
poolConfig.setMinIdle(config.getMinIdel());
poolConfig.setMaxIdle(config.getMaxIdel());
poolConfig.setMaxWaitMillis(config.getMaxWaitMillis());
this.internalPool = new GenericObjectPool<FTPClient>(factory, poolConfig);
this.internalPool.setTestOnBorrow(true);
log.info("連接池配置:{}", config.toString());
log.info("*********************初始化Ftp連接池完畢*******************");
}
/**
* 獲取FTPClient
* @return
*/
public FTPClient getFTPClient(){
try {
log.info("從連接池中獲取FtpClient");
return internalPool.borrowObject();
} catch (Exception e) {
log.error("從連接池獲取FTPClient失??!");
return null;
}
}
/**
* 歸還連接
* @param ftpClient
*/
public void returnFTPClient(FTPClient ftpClient) {
try {
ftpClient.getStatus();
log.info("歸還FtpClient:" + ftpClient);
internalPool.returnObject(ftpClient);
} catch (IOException e) {
log.info("移除過期ftpClient {}", e.getMessage());
try {
internalPool.invalidateObject(ftpClient);
} catch (Exception ex) {
log.info("移除過期ftpClient失敗 {}", e.getMessage());
}
}
}
/**
* 銷毀連接池
*/
public void destory() {
log.info("*********************close FtpPool**********************");
internalPool.close();
}
}FTP工具類
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
/**
* @author qf
* @version 1.0
* @data 2025/7/20 18:47
*/
@Lazy
@Slf4j
@Component
public class FTPClientUtil {
@Autowired
private FtpClientPool pool;
/**
* 上傳文件
*
* @param file
* @param path
* @return
* @throws IOException
*/
public Boolean upload(MultipartFile file, String path) throws IOException {
FTPClient ftpClient = pool.getFTPClient();
InputStream inputStream = null;
Boolean result = false;
try {
inputStream = file.getInputStream();
createDir(ftpClient, path);
result = ftpClient.storeFile(path, inputStream);
log.info("文件{},服務(wù)器路徑{},上傳結(jié)果result:{}", file.getOriginalFilename(), path, result);
} catch (IOException e) {
e.printStackTrace();
} finally {
inputStream.close();
pool.returnFTPClient(ftpClient);
}
return result;
}
/**
* 創(chuàng)建文件夾
*
* @param ftpClient
* @param path
* @return
* @throws IOException
*/
private Boolean createDir(FTPClient ftpClient, String path) throws IOException {
Boolean flag = false;
String substring = path.substring(path.indexOf("/") + 1, path.lastIndexOf("/"));
ftpClient.changeWorkingDirectory("/");
//boolean b = ftpClient.changeWorkingDirectory(substring);
String[] split = substring.split("/");
for (String s : split) {
flag = ftpClient.makeDirectory(s);
boolean changeFlag=ftpClient.changeWorkingDirectory(s);
log.info("目錄:{},創(chuàng)建目錄結(jié)果:{},切換目錄結(jié)果:{}",s,flag,changeFlag);
}
ftpClient.changeWorkingDirectory("/");
return flag;
}
}這里因為項目部署在多個服務(wù)器中,而有一些環(huán)境不使用ftp,因此使用懶加載的方式。
示例:
@Lazy
@Autowired
private FTPClientUtil ftpClientUtil;Controller
import com.qf.util.ftp.FTPClientUtil;
import com.qf.util.ftp.FTPConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
/**
* @author qf
* @version 1.0
* @data 2025/7/20 19:36
*/
@Slf4j
@RestController
@RequestMapping("api")
public class TestController {
@Autowired
private FTPConfig ftpConfig;
@Autowired
private FTPClientUtil ftpClientUtil;
@PostMapping(value = "/uploadExcelFile",headers = "content-type=multipart/form-data")
public Boolean uploadImgFile(@RequestParam("file") MultipartFile file) throws IOException {
String path = ftpConfig.getPath();
String originalFilename = file.getOriginalFilename();
String fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileId = UUID.randomUUID().toString().replace("-", "");
String newFileName = fileId + fileSuffix;
String filePath = path + newFileName;
Boolean flag = ftpClientUtil.upload(file, filePath);
return flag;
}
}到此這篇關(guān)于SpringBoot集成FTP上傳文件功能實現(xiàn)的文章就介紹到這了,更多相關(guān)springboot ftp上傳文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Intellij idea下使用不同tomcat編譯maven項目的服務(wù)器路徑方法詳解
今天小編就為大家分享一篇關(guān)于Intellij idea下使用不同tomcat編譯maven項目的服務(wù)器路徑方法詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02
Kotlin中使用Java數(shù)據(jù)類時引發(fā)的Bug解決方式
這篇文章主要介紹了Kotlin中使用Java數(shù)據(jù)類時引發(fā)的一個Bug,本文給大家分享問題解決方式,感興趣的朋友跟隨小編一起看看吧2023-09-09
SpringCloud微服務(wù)多應(yīng)用腳手架的搭建與部署方式
這篇文章主要介紹了SpringCloud微服務(wù)多應(yīng)用腳手架的搭建與部署方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
java讀取文件:char的ASCII碼值=65279,顯示是一個空字符的解決
這篇文章主要介紹了java讀取文件:char的ASCII碼值=65279,顯示是一個空字符的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

