使用Java连接FTP服务器进行文件相关操作,并且使用FTP连接池降低资源消耗,提高响应速率。
-
-
-
commons-net -
commons-net -
3.9.0 -
- 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
- 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
{ -
- /**
- * 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;
- }
- 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
{ - /**
- * 注入 ftp 连接配置
- */
- @Autowired
- FtpConfig config;
-
- /**
- * 创建连接到池中
- *
- * @return
- * @throws Exception
- */
- @Override
- public PooledObject
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
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
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
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
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;
- }
- }
-
- 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();
- }
-
- 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
pool; -
- /**
- * ftp 客户端配置文件
- */
- @Autowired
- private FtpConfig config;
-
- /**
- * ftp 客户端工厂
- */
- @Autowired
- private FTPClientFactory factory;
-
- /**
- * 初始化pool
- */
- @PostConstruct
- private void initPool() {
- this.pool = new GenericObjectPool
(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;
- }
- }
-
- 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") );
- 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") );
- // 定义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
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
getDirFileNum(String ftpPath) { - FTPClient ftpClient = ftpPoolService.borrowObject();
- try {
- Integer sum = 0;
- Map
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;
- }
- }
-