Java连接FTP服务器,并使用ftp连接池进行文件操作

顾十方 2024-09-05 13:05:02 阅读 86

使用Java连接FTP服务器进行文件相关操作,并且使用FTP连接池降低资源消耗,提高响应速率。

1、导入Pom依赖

<code> <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->

<dependency>

<groupId>commons-net</groupId>

<artifactId>commons-net</artifactId>

<version>3.9.0</version>

</dependency>

2、创建FTP的配置

ftp:

# 服务器地址

host: xx.xxx.xx.xxx

# 端口号

port: 21

# 用户名

userName: xxx

# 密码

password: xxxxxxx

# 工作目录

workingDirectory: /ftpTest

# 编码

encoding: utf-8

#被动模式

passiveMode: true

#连接超时时间

clientTimeout: 30000

# 线程数

threaNum: 1

# 0=ASCII_FILE_TYPE(ASCII格式),1=EBCDIC_FILE_TYPE,2=LOCAL_FILE_TYPE(二进制文件)

transferFileType: 2

# 是否重命名

renameUploaded: true

# 重新连接时间

retryTimes: 1200

# 缓存大小

bufferSize: 8192

# 最大数

maxTotal: 50

# 最小空闲

minldle: 10

# 最大空闲

maxldle: 50

# 最大等待时间

maxWait: 30000

# 池对象耗尽之后是否阻塞,maxWait < 0 时一直等待

blockWhenExhausted: true

# 取对象时验证

testOnBorrow: true

# 回收验证

testOnReturn: true

# 创建时验证

testOnCreate: true

# 空闲验证

testWhileldle: false

# 后进先出

lifo: false

3、创建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服务器地址

*/

private String host;

/**

* FTP服务器端口

*/

private Integer port;

/**

* FTP用户名

*/

private String userName;

/**

* FTP密码

*/

private String password;

/**

* FTP服务器根目录

*/

private String workingDirectory;

/**

* 传输编码

*/

String encoding;

/**

* 被动模式:在这种模式下,数据连接是由客户程序发起的

*/

boolean passiveMode;

/**

* 连接超时时间

*/

int clientTimeout;

/**

* 线程数

*/

int threaNum;

/**

* 0=ASCII_FILE_TYPE(ASCII格式),1=EBCDIC_FILE_TYPE,2=LOCAL_FILE_TYPE(二进制文件)

*/

int transferFileType;

/**

* 是否重命名

*/

boolean renameUploaded;

/**

* 重新连接时间

*/

int retryTimes;

/**

* 缓存大小

*/

int bufferSize;

/**

* 最大数

*/

int maxTotal;

/**

* 最小空闲

*/

int minldle;

/**

* 最大空闲

*/

int maxldle;

/**

* 最大等待时间

*/

int maxWait;

/**

* 池对象耗尽之后是否阻塞,maxWait < 0 时一直等待

*/

boolean blockWhenExhausted;

/**

* 取对象时验证

*/

boolean testOnBorrow;

/**

* 回收验证

*/

boolean testOnReturn;

/**

* 创建时验证

*/

boolean testOnCreate;

/**

* 空闲验证

*/

boolean testWhileldle;

/**

* 后进先出

*/

boolean lifo;

}

4、创建工厂连接对象并注入配置

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;

/**

* 创建连接到池中

*

* @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("创建ftp连接");

return new DefaultPooledObject<>(ftpClient);

}

/**

* 链接状态检查

*

* @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;

}

}

/**

* 销毁连接,当连接池空闲数量达到上限时,调用此方法销毁连接

*

* @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());

}

}

}

/**

* 钝化连接,是连接变为可用状态

*

* @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());

//设置上传文件类型为二进制,否则将无法打开文件

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

}

/**

* 获取 FTP 连接配置

* @return

*/

public FtpConfig getConfig(){

return config;

}

}

5、创建客户端对象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、创建FTP接口实现类 

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 上传结果

*/

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服务器上下载文件,支持断点续传,下载百分比汇报

*

* @param ftpPath 远程文件路径

* @param fileName 远程文件名

* @param local 本地文件完整绝对路径

* @return 下载的状态

* @throws IOException

*/

public DownloadStatus downloadFile(String ftpPath, String fileName, String local) throws IOException {

FTPClient ftpClient = ftpPoolService.borrowObject();

// 设置被动模式,由于Linux安全性考虑,端口没有全部放开,所有被动模式不能用

ftpClient.enterLocalPassiveMode();

// 设置以二进制方式传输

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;

}

// 进行断点续传,并记录状态

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服务器文件目录 *

* @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());

// 判断本地路径目录是否存在,不存在则创建

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服务器文件目录 *

* @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") );code>

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 = "导出数据.zip";

response.reset();

// 设置导出文件头

response.setContentType("application/octet-stream");

response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(zipFileName,"UTF-8") );code>

// 定义Zip输出流

zipOut = new ZipOutputStream(response.getOutputStream());

zipFTPFile(ftpPath,zipOut,"");

} catch (IOException e) {

log.error("当前:"+ftpPath+"下载FTP文件--->下载文件失败:"+e.getMessage());

} finally {

// 关闭zip文件输出流

if (null != zipOut) {

try {

zipOut.closeEntry();

zipOut.close();

} catch (IOException e) {

log.error("当前:"+ftpPath+"下载FTP文件--->关闭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("切换目录失败");

}

// 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据

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该文件把该文件转化为字节数组的输出流

ftpClient.retrieveFile(tempFile.getName(), os);

byte[] bytes = os.toByteArray();

InputStream ins = new ByteArrayInputStream(bytes);

int len;

zipOut.putNextEntry(new ZipEntry(foldPath + tempFile.getName()));

// 读入需要下载的文件的内容,打包到zip文件

while ((len = ins.read(byteReader)) > 0) {

zipOut.write(byteReader, 0, len);

}

}else{

//如果是文件夹,则递归调用该方法

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 服务器文件存储路径

* @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服务器,支持断点续传

* @param uploadPath 远程文件存放路径

* @param fileName 上传文件名

* @param input 文件输入流

* @return 上传结果

* @throws IOException

*/

public UploadStatus uploadFile(String uploadPath, String fileName, InputStream input) {

FTPClient ftpClient = ftpPoolService.borrowObject();

UploadStatus result = UploadStatus.UploadNewFileFailed;

try {

// 设置PassiveMode传输

ftpClient.enterLocalPassiveMode();

// 设置以二进制流的方式传输

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();

//根据文件输入流获取文件对象

File f = getFileFromInputStream(input);

long localSize = f.length();

// 文件存在

if (remoteSize == localSize) {

return UploadStatus.FileExits;

} else if (remoteSize > localSize) {

return UploadStatus.RemoteFileBiggerThanLocalFile;

}

// 尝试移动文件内读取指针,实现断点续传

result = uploadFile(fileName, f, ftpClient, remoteSize);

// 如果断点续传没有成功,则删除服务器上文件,重新上传

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 {

// 创建临时文件

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;

}

/**

* 递归创建远程服务器目录

*

* @param remote 远程服务器文件绝对路径

* @param ftpClient FTPClient对象

* @return 目录创建是否成功

* @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))) {

// 如果远程目录不存在,则递归创建远程服务器目录

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("创建目录失败");

return UploadStatus.CreateDirectoryFail;

}

}

start = end + 1;

end = directory.indexOf("/", start);

// 检查所有目录是否创建完毕

if (end <= start) {

break;

}

}

}

return status;

}

/**

* 上传文件到服务器,新上传和断点续传

*

* @param remoteFileName 远程文件名,在上传之前已经将服务器工作目录做了改变,一定要注意这里的 remoteFile 已经别被编码 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;

// 断点续传

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方法在执行ftpClient.completePendingCommand()之前应该先关闭OutputStream,否则主线程会在这里卡死执行不下去。

// 原因是completePendingCommand()会一直在等FTP Server返回226 Transfer complete,但是FTP Server只有在接受到OutputStream执行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某一特定目录下的文件数量

*

* @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;

}

/**

* 获取文件夹下文件数量

* @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服务器文件目录

* @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()) {

// 判断本地路径目录是否存在,不存在则创建

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服务器地址

* @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);

}

//创建文件夹

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;

}

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。