Java連接FTP服務(wù)器并使用ftp連接池進行文件操作指南
更新時間:2024年08月19日 09:55:50 作者:顧十方
使用FTP最主要的功能是對文件進行管理,下面這篇文章主要給大家介紹了關(guān)于Java連接FTP服務(wù)器并使用ftp連接池進行文件操作的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
前言
使用Java連接FTP服務(wù)器進行文件相關(guān)操作,并且使用FTP連接池降低資源消耗,提高響應(yīng)速率。
1、導(dǎo)入Pom依賴
<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.9.0</version>
</dependency>2、創(chuàng)建FTP的配置
ftp:
# 服務(wù)器地址
host: xx.xxx.xx.xxx
# 端口號
port: 21
# 用戶名
userName: xxx
# 密碼
password: xxxxxxx
# 工作目錄
workingDirectory: /ftpTest
# 編碼
encoding: utf-8
#被動模式
passiveMode: true
#連接超時時間
clientTimeout: 30000
# 線程數(shù)
threaNum: 1
# 0=ASCII_FILE_TYPE(ASCII格式),1=EBCDIC_FILE_TYPE,2=LOCAL_FILE_TYPE(二進制文件)
transferFileType: 2
# 是否重命名
renameUploaded: true
# 重新連接時間
retryTimes: 1200
# 緩存大小
bufferSize: 8192
# 最大數(shù)
maxTotal: 50
# 最小空閑
minldle: 10
# 最大空閑
maxldle: 50
# 最大等待時間
maxWait: 30000
# 池對象耗盡之后是否阻塞,maxWait < 0 時一直等待
blockWhenExhausted: true
# 取對象時驗證
testOnBorrow: true
# 回收驗證
testOnReturn: true
# 創(chuàng)建時驗證
testOnCreate: true
# 空閑驗證
testWhileldle: false
# 后進先出
lifo: false3、創(chuàng)建FTP配置類
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.apache.commons.net.ftp.FTPClient;
/**
* Ftp配置類
*/
@Configuration
@ConfigurationProperties(prefix = "ftp")
@Getter
@Setter
public class FtpConfig extends GenericObjectPoolConfig<FTPClient> {
/**
* FTP服務(wù)器地址
*/
private String host;
/**
* FTP服務(wù)器端口
*/
private Integer port;
/**
* FTP用戶名
*/
private String userName;
/**
* FTP密碼
*/
private String password;
/**
* FTP服務(wù)器根目錄
*/
private String workingDirectory;
/**
* 傳輸編碼
*/
String encoding;
/**
* 被動模式:在這種模式下,數(shù)據(jù)連接是由客戶程序發(fā)起的
*/
boolean passiveMode;
/**
* 連接超時時間
*/
int clientTimeout;
/**
* 線程數(shù)
*/
int threaNum;
/**
* 0=ASCII_FILE_TYPE(ASCII格式),1=EBCDIC_FILE_TYPE,2=LOCAL_FILE_TYPE(二進制文件)
*/
int transferFileType;
/**
* 是否重命名
*/
boolean renameUploaded;
/**
* 重新連接時間
*/
int retryTimes;
/**
* 緩存大小
*/
int bufferSize;
/**
* 最大數(shù)
*/
int maxTotal;
/**
* 最小空閑
*/
int minldle;
/**
* 最大空閑
*/
int maxldle;
/**
* 最大等待時間
*/
int maxWait;
/**
* 池對象耗盡之后是否阻塞,maxWait < 0 時一直等待
*/
boolean blockWhenExhausted;
/**
* 取對象時驗證
*/
boolean testOnBorrow;
/**
* 回收驗證
*/
boolean testOnReturn;
/**
* 創(chuàng)建時驗證
*/
boolean testOnCreate;
/**
* 空閑驗證
*/
boolean testWhileldle;
/**
* 后進先出
*/
boolean lifo;
}
4、創(chuàng)建工廠連接對象并注入配置
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
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;
/**
* FtpClient 工廠連接對象
*/
@Component
@Slf4j
public class FTPClientFactory implements PooledObjectFactory<FTPClient> {
/**
* 注入 ftp 連接配置
*/
@Autowired
FtpConfig config;
/**
* 創(chuàng)建連接到池中
*
* @return
* @throws Exception
*/
@Override
public PooledObject<FTPClient> makeObject() throws Exception {
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(config.getClientTimeout());
ftpClient.connect(config.getHost(), config.getPort());
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
return null;
}
boolean success;
if (StringUtils.isBlank(config.getUserName())) {
success = ftpClient.login("anonymous", "anonymous");
} else {
success = ftpClient.login(config.getUserName(), config.getPassword());
}
if (!success) {
return null;
}
ftpClient.setFileType(config.getTransferFileType());
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding(config.getEncoding());
if (config.isPassiveMode()) {
ftpClient.enterLocalPassiveMode();
}
log.debug("創(chuàng)建ftp連接");
return new DefaultPooledObject<>(ftpClient);
}
/**
* 鏈接狀態(tài)檢查
*
* @param pool
* @return
*/
@Override
public boolean validateObject(PooledObject<FTPClient> pool) {
FTPClient ftpClient = pool.getObject();
try {
return ftpClient != null && ftpClient.sendNoOp();
} catch (Exception e) {
return false;
}
}
/**
* 銷毀連接,當連接池空閑數(shù)量達到上限時,調(diào)用此方法銷毀連接
*
* @param pool
* @throws Exception
*/
@Override
public void destroyObject(PooledObject<FTPClient> pool) throws Exception {
FTPClient ftpClient = pool.getObject();
if (ftpClient != null) {
try {
ftpClient.disconnect();
log.debug("銷毀ftp連接");
} catch (Exception e) {
log.error("銷毀ftpClient異常,error:", e.getMessage());
}
}
}
/**
* 鈍化連接,是連接變?yōu)榭捎脿顟B(tài)
*
* @param p
* @throws Exception
*/
@Override
public void passivateObject(PooledObject<FTPClient> p) throws Exception{
FTPClient ftpClient = p.getObject();
try {
ftpClient.changeWorkingDirectory(config.getWorkingDirectory());
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
} catch (Exception e) {
throw new RuntimeException("Could not disconnect from server.", e);
}
}
/**
* 初始化連接
*
* @param pool
* @throws Exception
*/
@Override
public void activateObject(PooledObject<FTPClient> pool) throws Exception {
FTPClient ftpClient = pool.getObject();
ftpClient.connect(config.getHost(),config.getPort());
ftpClient.login(config.getUserName(), config.getPassword());
ftpClient.setControlEncoding(config.getEncoding());
ftpClient.changeWorkingDirectory(config.getWorkingDirectory());
//設(shè)置上傳文件類型為二進制,否則將無法打開文件
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
}
/**
* 獲取 FTP 連接配置
* @return
*/
public FtpConfig getConfig(){
return config;
}
}5、創(chuàng)建客戶端對象service接口
import com.aicut.monitor.config.FtpConfig;
import org.apache.commons.net.ftp.FTPClient;
/**
* 獲取 ftp 客戶端對象的接口
*/
public interface FTPPoolService {
/**
* 獲取ftpClient
* @return
*/
FTPClient borrowObject();
/**
* 歸還ftpClient
* @param ftpClient
* @return
*/
void returnObject(FTPClient ftpClient);
/**
* 獲取 ftp 配置信息
* @return
*/
FtpConfig getFtpPoolConfig();
}6、創(chuàng)建FTP接口實現(xiàn)類
import com.aicut.monitor.config.FTPClientFactory;
import com.aicut.monitor.config.FtpConfig;
import com.aicut.monitor.service.FTPPoolService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
@Slf4j
public class FTPPoolServiceImpl implements FTPPoolService {
/**
* ftp 連接池生成
*/
private GenericObjectPool<FTPClient> pool;
/**
* ftp 客戶端配置文件
*/
@Autowired
private FtpConfig config;
/**
* ftp 客戶端工廠
*/
@Autowired
private FTPClientFactory factory;
/**
* 初始化pool
*/
@PostConstruct
private void initPool() {
this.pool = new GenericObjectPool<FTPClient>(this.factory, this.config);
}
/**
* 獲取ftpClient
*/
@Override
public FTPClient borrowObject() {
if (this.pool != null) {
try {
return this.pool.borrowObject();
} catch (Exception e) {
log.error("獲取 FTPClient 失敗 ", e);
}
}
return null;
}
/**
* 歸還 ftpClient
*/
@Override
public void returnObject(FTPClient ftpClient) {
if (this.pool != null && ftpClient != null) {
this.pool.returnObject(ftpClient);
}
}
@Override
public FtpConfig getFtpPoolConfig() {
return config;
}
}7、FTP工具類
import cn.hutool.core.util.CharsetUtil;
import com.aicut.monitor.enums.DownloadStatus;
import com.aicut.monitor.enums.UploadStatus;
import com.aicut.monitor.enums.uploadImageType;
import com.aicut.monitor.service.FTPPoolService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
import org.apache.commons.compress.archivers.zip.UnixStat;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.NullInputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* FTP工具類
*
* @author YJ2023085043
*/
@Component
@Slf4j
public class FtpUtil {
/**
* ftp 連接池
*/
@Autowired
FTPPoolService ftpPoolService;
public static final String DIR_SPLIT = "/";
public static final String HTTP_protocol = "http://";
/**
* 上傳單個文件
*
* @param uploadPath 上傳路徑
* @param fileName 文件名
* @param input 文件輸入流
* @return 上傳結(jié)果
*/
public UploadStatus upload(String uploadPath, String fileName, InputStream input) {
FTPClient ftpClient = ftpPoolService.borrowObject();
try {
// 切換到工作目錄
if (!ftpClient.changeWorkingDirectory(uploadPath)) {
ftpClient.makeDirectory(uploadPath);
ftpClient.changeWorkingDirectory(uploadPath);
}
// 文件寫入
boolean storeFile = ftpClient.storeFile(fileName, input);
if (storeFile) {
log.info("文件:{}上傳成功", fileName);
return UploadStatus.UploadNewFileSuccess;
} else {
throw new RuntimeException("ftp文件寫入異常");
}
} catch (IOException e) {
log.error("文件:{}上傳失敗", fileName, e);
return UploadStatus.UploadNewFileFailed;
} finally {
IOUtils.closeQuietly(input);
ftpPoolService.returnObject(ftpClient);
}
}
/**
* 從FTP服務(wù)器上下載文件,支持斷點續(xù)傳,下載百分比匯報
*
* @param ftpPath 遠程文件路徑
* @param fileName 遠程文件名
* @param local 本地文件完整絕對路徑
* @return 下載的狀態(tài)
* @throws IOException
*/
public DownloadStatus downloadFile(String ftpPath, String fileName, String local) throws IOException {
FTPClient ftpClient = ftpPoolService.borrowObject();
// 設(shè)置被動模式,由于Linux安全性考慮,端口沒有全部放開,所有被動模式不能用
ftpClient.enterLocalPassiveMode();
// 設(shè)置以二進制方式傳輸
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
DownloadStatus result;
try {
// 檢查遠程文件是否存在
FTPFile[] files = ftpClient.listFiles(ftpPath,file -> file.getName().equals(fileName));
if (files.length != 1) {
log.info("遠程文件不存在");
return DownloadStatus.RemoteFileNotExist;
}
long lRemoteSize = files[0].getSize();
File f = new File(local+DIR_SPLIT+fileName);
// 本地存在文件,進行斷點下載
if (f.exists()) {
long localSize = f.length();
// 判斷本地文件大小是否大于遠程文件大小
if (localSize >= lRemoteSize) {
log.info("本地文件大于遠程文件,下載中止");
return DownloadStatus.LocalFileBiggerThanRemoteFile;
}
// 進行斷點續(xù)傳,并記錄狀態(tài)
FileOutputStream out = new FileOutputStream(f, true);
ftpClient.setRestartOffset(localSize);
InputStream in = ftpClient.retrieveFileStream(ftpPath + DIR_SPLIT + fileName);
byte[] bytes = new byte[1024];
long step = lRemoteSize / 100;
// 文件過小,step可能為0
step = step == 0 ? 1 : step;
long process = localSize / step;
int c;
while ((c = in.read(bytes)) != -1) {
out.write(bytes, 0, c);
localSize += c;
long nowProcess = localSize / step;
if (nowProcess > process) {
process = nowProcess;
if (process % 10 == 0) {
log.info("下載進度:" + process);
}
}
}
in.close();
out.close();
boolean isDo = ftpClient.completePendingCommand();
if (isDo) {
result = DownloadStatus.DownloadFromBreakSuccess;
} else {
result = DownloadStatus.DownloadFromBreakFailed;
}
} else {
OutputStream out = new FileOutputStream(f);
InputStream in = ftpClient.retrieveFileStream(ftpPath + DIR_SPLIT + fileName);
byte[] bytes = new byte[1024];
long step = lRemoteSize / 100;
// 文件過小,step可能為0
step = step == 0 ? 1 : step;
long process = 0;
long localSize = 0L;
int c;
while ((c = in.read(bytes)) != -1) {
out.write(bytes, 0, c);
localSize += c;
long nowProcess = localSize / step;
if (nowProcess > process) {
process = nowProcess;
if (process % 10 == 0) {
log.info("下載進度:" + process);
}
}
}
in.close();
out.close();
boolean upNewStatus = ftpClient.completePendingCommand();
if (upNewStatus) {
result = DownloadStatus.DownloadNewSuccess;
} else {
result = DownloadStatus.DownloadNewFailed;
}
}
} catch (Exception e) {
log.error("download error", e);
} finally {
ftpPoolService.returnObject(ftpClient);
}
return DownloadStatus.DownloadNewFailed;
}
/**
* 下載文件到本地 *
*
* @param ftpPath FTP服務(wù)器文件目錄 *
* @param ftpFileName 文件名稱 *
* @param localPath 下載后的文件路徑 *
* @return
*/
public boolean download(String ftpPath, String ftpFileName, String localPath) {
FTPClient ftpClient = ftpPoolService.borrowObject();
OutputStream outputStream = null;
try {
FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.isFile() && file.getName().equals(ftpFileName));
if (ftpFiles != null && ftpFiles.length > 0) {
FTPFile ftpFile = ftpFiles[0];
File localFile = new File(localPath + DIR_SPLIT + ftpFile.getName());
// 判斷本地路徑目錄是否存在,不存在則創(chuàng)建
if (!localFile.getParentFile().exists()) {
localFile.getParentFile().mkdirs();
}
outputStream = Files.newOutputStream(localFile.toPath());
ftpClient.retrieveFile(ftpFile.getName(), outputStream);
log.info("fileName:{},size:{}", ftpFile.getName(), ftpFile.getSize());
log.info("下載文件成功...");
return true;
} else {
log.info("文件不存在,filePathname:{},", ftpPath + DIR_SPLIT + ftpFileName);
}
} catch (Exception e) {
log.error("下載文件失敗...",e);
} finally {
IOUtils.closeQuietly(outputStream);
ftpPoolService.returnObject(ftpClient);
}
return false;
}
/**
* 下載文件到瀏覽器 *
*
* @param ftpPath FTP服務(wù)器文件目錄 *
* @param ftpFileName 文件名稱 *
* @param response
* @return
*/
public void download(HttpServletResponse response, String ftpPath, String ftpFileName) {
FTPClient ftpClient = ftpPoolService.borrowObject();
OutputStream outputStream = null;
try {
FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.isFile() && file.getName().equals(ftpFileName));
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf8");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(ftpFileName,"UTF-8") );
outputStream = response.getOutputStream();
if (ftpFiles != null && ftpFiles.length > 0) {
FTPFile ftpFile = ftpFiles[0];
ftpClient.retrieveFile(ftpPath+DIR_SPLIT+ftpFile.getName(), outputStream);
log.info("fileName:{},size:{}", ftpFile.getName(), ftpFile.getSize());
log.info("下載文件成功...");
} else {
log.info("文件不存在,filePathname:{},", ftpPath + DIR_SPLIT + ftpFileName);
}
} catch (Exception e) {
log.error("下載文件失敗...",e);
} finally {
IOUtils.closeQuietly(outputStream);
ftpPoolService.returnObject(ftpClient);
}
}
public void ftpZipFileDownload (HttpServletResponse response,String ftpPath) {
//從FTP上下載文件并打成ZIP包給用戶下載
ZipOutputStream zipOut = null;
try {
//文件名稱
String zipFileName = "導(dǎo)出數(shù)據(jù).zip";
response.reset();
// 設(shè)置導(dǎo)出文件頭
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(zipFileName,"UTF-8") );
// 定義Zip輸出流
zipOut = new ZipOutputStream(response.getOutputStream());
zipFTPFile(ftpPath,zipOut,"");
} catch (IOException e) {
log.error("當前:"+ftpPath+"下載FTP文件--->下載文件失?。?+e.getMessage());
} finally {
// 關(guān)閉zip文件輸出流
if (null != zipOut) {
try {
zipOut.closeEntry();
zipOut.close();
} catch (IOException e) {
log.error("當前:"+ftpPath+"下載FTP文件--->關(guān)閉zip文件輸出流出錯:"+e.getMessage());
}
}
}
}
public void zipFTPFile(String ftpPath, ZipOutputStream zipOut,String foldPath){
FTPClient ftpClient = ftpPoolService.borrowObject();
try {
// 切換到指定目錄中,如果切換失敗說明目錄不存在
if(!ftpClient.changeWorkingDirectory(ftpPath)){
log.error("切換目錄失敗");
throw new RuntimeException("切換目錄失敗");
}
// 每次數(shù)據(jù)連接之前,ftp client告訴ftp server開通一個端口來傳輸數(shù)據(jù)
ftpClient.enterLocalPassiveMode();
// 遍歷路徑下的所有文件
FTPFile[] fileList = ftpClient.listFiles();
byte[] byteReader = new byte[1024];
ByteArrayOutputStream os = null;
for (FTPFile tempFile : fileList) {
if (tempFile.isFile()) {
os = new ByteArrayOutputStream();
// 從FTP上下載downFileName該文件把該文件轉(zhuǎn)化為字節(jié)數(shù)組的輸出流
ftpClient.retrieveFile(tempFile.getName(), os);
byte[] bytes = os.toByteArray();
InputStream ins = new ByteArrayInputStream(bytes);
int len;
zipOut.putNextEntry(new ZipEntry(foldPath + tempFile.getName()));
// 讀入需要下載的文件的內(nèi)容,打包到zip文件
while ((len = ins.read(byteReader)) > 0) {
zipOut.write(byteReader, 0, len);
}
}else{
//如果是文件夾,則遞歸調(diào)用該方法
zipOut.putNextEntry(new ZipEntry(tempFile.getName() + DIR_SPLIT));
zipFTPFile(ftpPath + DIR_SPLIT + tempFile.getName(), zipOut, tempFile.getName() + DIR_SPLIT);
}
}
zipOut.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
ftpPoolService.returnObject(ftpClient);
}
}
/**
* 得到某個目錄下的文件名列表
*
* @param ftpDirPath FTP上的目標文件路徑
* @return
* @throws IOException
*/
public List<String> getFileList(String ftpDirPath) {
FTPClient ftpClient = ftpPoolService.borrowObject();
try {
FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath);
if (ftpFiles != null && ftpFiles.length > 0) {
return Arrays.stream(ftpFiles).map(FTPFile::getName).collect(Collectors.toList());
}
log.error(String.format("路徑有誤,或目錄【%s】為空", ftpDirPath));
} catch (Exception e) {
log.error("獲取目錄下文件列表失敗", e);
} finally {
ftpPoolService.returnObject(ftpClient);
}
return null;
}
/**
* 刪除文件
*
* @param ftpPath 服務(wù)器文件存儲路徑
* @param fileName 文件名
* @return
* @throws IOException
*/
public boolean deleteFile(String ftpPath, String fileName) {
FTPClient ftpClient = ftpPoolService.borrowObject();
try {
// 在 ftp 目錄下獲取文件名與 fileName 匹配的文件信息
FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.getName().equals(fileName));
// 刪除文件
if (ftpFiles != null && ftpFiles.length > 0) {
boolean del;
String deleteFilePath = ftpPath + DIR_SPLIT + fileName;
FTPFile ftpFile = ftpFiles[0];
if (ftpFile.isDirectory()) {
//遞歸刪除該目錄下的所有文件后刪除目錄
FTPFile[] files = ftpClient.listFiles(ftpPath + DIR_SPLIT + fileName);
for (FTPFile file : files) {
if(file.isDirectory()){
deleteFile(ftpPath + DIR_SPLIT + fileName,file.getName());
}else{
del = ftpClient.deleteFile(deleteFilePath + DIR_SPLIT + file.getName());
log.info(del ? "文件:{}刪除成功" : "文件:{}刪除失敗", file.getName());
}
}
del = ftpClient.removeDirectory(deleteFilePath);
} else {
del = ftpClient.deleteFile(deleteFilePath);
}
log.info(del ? "文件:{}刪除成功" : "文件:{}刪除失敗", fileName);
return del;
} else {
log.warn("文件:{}未找到", fileName);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
ftpPoolService.returnObject(ftpClient);
}
return false;
}
/**
* 上傳文件到FTP服務(wù)器,支持斷點續(xù)傳
* @param uploadPath 遠程文件存放路徑
* @param fileName 上傳文件名
* @param input 文件輸入流
* @return 上傳結(jié)果
* @throws IOException
*/
public UploadStatus uploadFile(String uploadPath, String fileName, InputStream input) {
FTPClient ftpClient = ftpPoolService.borrowObject();
UploadStatus result = UploadStatus.UploadNewFileFailed;
try {
// 設(shè)置PassiveMode傳輸
ftpClient.enterLocalPassiveMode();
// 設(shè)置以二進制流的方式傳輸
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setControlEncoding(CharsetUtil.UTF_8);
//切換到工作目錄
if(!ftpClient.changeWorkingDirectory(uploadPath)){
ftpClient.makeDirectory(uploadPath);
ftpClient.changeWorkingDirectory(uploadPath);
}
// 檢查遠程是否存在文件
FTPFile[] files = ftpClient.listFiles(uploadPath,file -> file.getName().equals(fileName));
if (files.length == 1) {
long remoteSize = files[0].getSize();
//根據(jù)文件輸入流獲取文件對象
File f = getFileFromInputStream(input);
long localSize = f.length();
// 文件存在
if (remoteSize == localSize) {
return UploadStatus.FileExits;
} else if (remoteSize > localSize) {
return UploadStatus.RemoteFileBiggerThanLocalFile;
}
// 嘗試移動文件內(nèi)讀取指針,實現(xiàn)斷點續(xù)傳
result = uploadFile(fileName, f, ftpClient, remoteSize);
// 如果斷點續(xù)傳沒有成功,則刪除服務(wù)器上文件,重新上傳
if (result == UploadStatus.UploadFromBreakFailed) {
if (!ftpClient.deleteFile(fileName)) {
return UploadStatus.DeleteRemoteFaild;
}
result = uploadFile(fileName, f, ftpClient, 0);
}
} else {
result = uploadFile(fileName, getFileFromInputStream(input), ftpClient, 0);
}
} catch (Exception e) {
log.error("上傳文件失敗", e);
} finally {
ftpPoolService.returnObject(ftpClient);
}
return result;
}
/**
* 從輸入流中獲取文件對象
* @param inputStream
* @return
*/
public static File getFileFromInputStream(InputStream inputStream) {
File file = null;
try {
// 創(chuàng)建臨時文件
file = File.createTempFile("temp", null);
// 將輸入流寫入臨時文件
byte[] buffer = new byte[1024];
int bytesRead;
try (FileOutputStream outputStream = new FileOutputStream(file)) {
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
/**
* 遞歸創(chuàng)建遠程服務(wù)器目錄
*
* @param remote 遠程服務(wù)器文件絕對路徑
* @param ftpClient FTPClient對象
* @return 目錄創(chuàng)建是否成功
* @throws IOException
*/
public UploadStatus createDirectory(String remote, FTPClient ftpClient) throws IOException {
UploadStatus status = UploadStatus.CreateDirectorySuccess;
String directory = remote.substring(0, remote.lastIndexOf("/") + 1);
if (!directory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(new String(directory.getBytes(CharsetUtil.UTF_8), CharsetUtil.ISO_8859_1))) {
// 如果遠程目錄不存在,則遞歸創(chuàng)建遠程服務(wù)器目錄
int start = 0;
int end = 0;
if (directory.startsWith("/")) {
start = 1;
} else {
start = 0;
}
end = directory.indexOf("/", start);
while (true) {
String subDirectory = new String(remote.substring(start, end).getBytes(CharsetUtil.UTF_8), CharsetUtil.ISO_8859_1);
if (!ftpClient.changeWorkingDirectory(subDirectory)) {
if (ftpClient.makeDirectory(subDirectory)) {
ftpClient.changeWorkingDirectory(subDirectory);
} else {
log.info("創(chuàng)建目錄失敗");
return UploadStatus.CreateDirectoryFail;
}
}
start = end + 1;
end = directory.indexOf("/", start);
// 檢查所有目錄是否創(chuàng)建完畢
if (end <= start) {
break;
}
}
}
return status;
}
/**
* 上傳文件到服務(wù)器,新上傳和斷點續(xù)傳
*
* @param remoteFileName 遠程文件名,在上傳之前已經(jīng)將服務(wù)器工作目錄做了改變,一定要注意這里的 remoteFile 已經(jīng)別被編碼 ISO-8859-1
* @param localFile 本地文件File句柄,絕對路徑
* @param ftpClient FTPClient引用
* @return
* @throws IOException
*/
public UploadStatus uploadFile(String remoteFileName, File localFile, FTPClient ftpClient, long remoteSize) {
if (null == ftpClient) {
ftpClient = ftpPoolService.borrowObject();
}
if (null == ftpClient) {
return null;
}
UploadStatus status = UploadStatus.UploadNewFileFailed;
try (RandomAccessFile raf = new RandomAccessFile(localFile, "r");
OutputStream out = ftpClient.appendFileStream(remoteFileName);) {
// 顯示進度的上傳
log.info("localFile.length():" + localFile.length());
long step = localFile.length() / 100;
// 文件過小,step可能為0
step = step == 0 ? 1 : step;
long process = 0;
long localreadbytes = 0L;
// 斷點續(xù)傳
if (remoteSize > 0) {
ftpClient.setRestartOffset(remoteSize);
process = remoteSize / step;
raf.seek(remoteSize);
localreadbytes = remoteSize;
}
byte[] bytes = new byte[1024];
int c;
while ((c = raf.read(bytes)) != -1) {
out.write(bytes, 0, c);
localreadbytes += c;
if (localreadbytes / step != process) {
process = localreadbytes / step;
if (process % 10 == 0) {
log.info("上傳進度:" + process);
}
}
}
out.flush();
raf.close();
out.close();
// FTPUtil的upload方法在執(zhí)行ftpClient.completePendingCommand()之前應(yīng)該先關(guān)閉OutputStream,否則主線程會在這里卡死執(zhí)行不下去。
// 原因是completePendingCommand()會一直在等FTP Server返回226 Transfer complete,但是FTP Server只有在接受到OutputStream執(zhí)行close方法時,才會返回。
boolean result = ftpClient.completePendingCommand();
if (remoteSize > 0) {
status = result ? UploadStatus.UploadFromBreakSuccess : UploadStatus.UploadFromBreakFailed;
} else {
status = result ? UploadStatus.UploadNewFileSuccess : UploadStatus.UploadNewFileFailed;
}
} catch (Exception e) {
log.error("uploadFile error ", e);
}
return status;
}
/**
* 獲取FTP某一特定目錄下的文件數(shù)量
*
* @param ftpDirPath FTP上的目標文件路徑
*/
public Integer getFileNum(String ftpDirPath) {
FTPClient ftpClient = ftpPoolService.borrowObject();
try {
FTPFile[] ftpFiles = ftpClient.listFiles(ftpDirPath);
if (ftpFiles != null && ftpFiles.length > 0) {
return Arrays.stream(ftpFiles).map(FTPFile::getName).collect(Collectors.toList()).size();
}
log.error(String.format("路徑有誤,或目錄【%s】為空", ftpDirPath));
} catch (IOException e) {
log.error("文件獲取異常:", e);
} finally {
ftpPoolService.returnObject(ftpClient);
}
return null;
}
/**
* 獲取文件夾下文件數(shù)量
* @param ftpPath
* @return
*/
public Map<String,String> getDirFileNum(String ftpPath) {
FTPClient ftpClient = ftpPoolService.borrowObject();
try {
Integer sum = 0;
Map<String,String> map = new HashMap<>();
FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath);
if (ftpFiles != null && ftpFiles.length > 0) {
for (FTPFile file : ftpFiles) {
if (file.isDirectory()) {
sum += getFileNum(ftpPath + DIR_SPLIT + file.getName());
map.put(file.getName(), String.valueOf(getFileNum(ftpPath + DIR_SPLIT + file.getName())));
}
}
}else {
log.error(String.format("路徑有誤,或目錄【%s】為空", ftpPath));
}
map.put("sum", String.valueOf(sum));
return map;
} catch (IOException e) {
log.error("文件獲取異常:", e);
} finally {
ftpPoolService.returnObject(ftpClient);
}
return null;
}
/**
* 下載指定文件夾到本地
* @param ftpPath FTP服務(wù)器文件目錄
* @param localPath 下載后的文件路徑
* @param dirName 文件夾名稱
* @return
*/
public void downloadDir(String ftpPath, String localPath, String dirName){
FTPClient ftpClient = ftpPoolService.borrowObject();
OutputStream outputStream = null;
try {
//判斷是否存在該文件夾
FTPFile[] ftpFiles = ftpClient.listFiles(ftpPath, file -> file.getName().equals(dirName));
if (ftpFiles != null && ftpFiles.length > 0) {
if(ftpFiles[0].isDirectory()) {
// 判斷本地路徑目錄是否存在,不存在則創(chuàng)建
File localFile = new File(localPath + DIR_SPLIT + dirName);
if (!localFile.exists()) {
localFile.mkdirs();
}
for (FTPFile file : ftpClient.listFiles(ftpPath + DIR_SPLIT + dirName)) {
if (file.isDirectory()) {
downloadDir(ftpPath + DIR_SPLIT + dirName, localPath + dirName + DIR_SPLIT, file.getName());
} else {
outputStream = Files.newOutputStream(new File(localPath + DIR_SPLIT + dirName + DIR_SPLIT + file.getName()).toPath());
ftpClient.retrieveFile(ftpPath + DIR_SPLIT + dirName + DIR_SPLIT + file.getName(), outputStream);
log.info("fileName:{},size:{}", file.getName(), file.getSize());
outputStream.close();
}
}
}
}
}catch (Exception e){
log.error("下載文件夾失敗,filePathname:{},", ftpPath + DIR_SPLIT + dirName, e);
}finally {
IOUtils.closeQuietly(outputStream);
ftpPoolService.returnObject(ftpClient);
}
}
/**
* 從本地上傳文件夾
* @param uploadPath ftp服務(wù)器地址
* @param localPath 本地文件夾地址
* @param dirName 文件夾名稱
* @return
*/
public boolean uploadDir(String uploadPath, String localPath, String dirName){
FTPClient ftpClient = ftpPoolService.borrowObject();
try{
// 切換到工作目錄
if (!ftpClient.changeWorkingDirectory(uploadPath)) {
ftpClient.makeDirectory(uploadPath);
ftpClient.changeWorkingDirectory(uploadPath);
}
//創(chuàng)建文件夾
ftpClient.makeDirectory(uploadPath + DIR_SPLIT + dirName);
File src = new File(localPath);
//獲取該目錄下的所有文件
File[] files = src.listFiles();
FileInputStream input = null;
for(File file : files) {
if (file.isDirectory()) {
uploadDir(uploadPath + DIR_SPLIT + dirName, file.getAbsolutePath(), file.getName());
} else {
input = new FileInputStream(file);
boolean storeFile = ftpClient.storeFile(uploadPath + DIR_SPLIT + dirName + DIR_SPLIT + file.getName(), input);
if (storeFile) {
log.info("文件:{}上傳成功", file.getName());
} else {
throw new RuntimeException("ftp文件寫入異常");
}
}
}
if(input != null){
input.close();
}
}catch (Exception e){
log.error("文件夾上傳失敗",e);
}finally {
ftpPoolService.returnObject(ftpClient);
}
return false;
}
}總結(jié)
到此這篇關(guān)于Java連接FTP服務(wù)器并使用ftp連接池進行文件操作指南的文章就介紹到這了,更多相關(guān)Java連接FTP服務(wù)器進行文件操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot的@EnableAsync和@Async注解分析
這篇文章主要介紹了SpringBoot的@EnableAsync和@Async注解分析,Spring Boot是一個快速開發(fā)框架,可以幫助開發(fā)人員快速構(gòu)建基于Spring的應(yīng)用程序,需要的朋友可以參考下2023-07-07
java實現(xiàn)可視化界面肯德基(KFC)點餐系統(tǒng)代碼實例
這篇文章主要介紹了java肯德基點餐系統(tǒng),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
IDEA 中使用 Big Data Tools 連接大數(shù)據(jù)組件
本文主要介紹了IDEA 中使用 Big Data Tools 連接大數(shù)據(jù)組件,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
Java網(wǎng)絡(luò)編程之TCP通信完整代碼示例
這篇文章主要介紹了Java網(wǎng)絡(luò)編程之TCP通信完整代碼示例,具有一定借鑒價值,需要的朋友可以了解下。2017-12-12

