目录结构
application.yml
# 配置ftp服务器信息
ftp:
# ftp服务器的IP地址
url:
# 默认端口是21
port: 21
username:
password:
# ftp服务器存放文件的路径
remotePath: /case
# 本地需要上传的文件的路径
localDir: D:/test/case/push
# ftp上文件下载到本地存放的路径
downDir: D:/test/case/receive
# ftp下载zip后解压存放的位置
unzipDir: D:/test/case/unzip
FtpConfig
package com.vxdata.cases.modules.ftp.config;
import lombok.Data;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author :mmzsit
* @description:ftp服务器相关配置信息
* @date :2018/03/24 14:05
*/
@Component
@Data
public class FtpConfig {
/**
* ftp服务器地址
*/
@Value("${ftp.url}")
private String url;
/**
* ftp服务器端口
*/
@Value("${ftp.port}")
private int port;
/**
* ftp服务器用户名
*/
@Value("${ftp.username}")
private String username;
/**
* ftp服务器密码
*/
@Value("${ftp.password}")
private String password;
/**
* ftp服务器存放文件的路径
*/
@Value("${ftp.remotePath}")
private String remotePath;
/**
* 本地需要上传的文件的路径
*/
@Value("${ftp.localDir}")
private String localDir;
/**
* 下载文件时,存放在本地的路径
*/
@Value("${ftp.downDir}")
private String downDir;
@Value("${ftp.unzipDir}")
private String unzipDir;
}
FtpUtil
package com.vxdata.cases.modules.ftp.utils;
import com.vxdata.cases.modules.ftp.config.FtpConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.*;
import org.apache.poi.hssf.record.DVALRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.SocketException;
@Slf4j(topic="文件上传/下载===ftp服务器:")
@Component
public class FtpUtil {
private static FTPClient mFTPClient = new FTPClient();
private static FtpUtil ftp = new FtpUtil();
private static FtpConfig ftpConfig;
@Autowired
public void init(FtpConfig ftpConfig) {
FtpUtil.ftpConfig = ftpConfig;
}
/**
* 上传文件到ftp服务器
* @param localPath 本地文件地址
* @param ftpRemotePath 远程地址
* @return
*/
public static boolean ftpUpload(String fileName, String localPath, String ftpRemotePath) {
boolean result = false;
try {
// 链接到ftp服务器
boolean isConnection = ftp.openConnection();
if (isConnection) {
// 上传文件
boolean isSuccess = ftp.uploadByPath(ftpRemotePath, fileName);
if (isSuccess) {
log.info("FTP文件上传成功!");
result = true;
} else {
log.info("FTP文件上传失败!");
result = false;
}
// 登出并断开连接
ftp.logout();
} else {
log.info("链接ftp服务器失败,请联系管理员检查配置信息是否正确!");
result = false;
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 从ftp服务器下载文件到本地
* @param fileName 要下载的远程文件
* @param ftpRemotePath ftp服务器存放文件的路径
* @return
*/
public static boolean ftpDownload(String fileName, String localPath, String ftpRemotePath) {
boolean result = false;
try {
// 连接ftp服务
boolean isConnection = ftp.openConnection();
if (isConnection) {
// 创建远程目录
boolean isCreateOk = ftp.createDirectory(ftpRemotePath, mFTPClient);
// 下载文件到本地地址
boolean isDownloadOk = ftp.downLoad(ftpRemotePath +File.separator+fileName, localPath);
if (isDownloadOk && isCreateOk) {
result = true;
}
ftp.logout();
} else {
log.info("链接ftp服务器失败,请检查配置信息是否正确!");
result = false;
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 上传本地文件
* @param remotePath 远程地址
* @param localPath 本地文件地址
* @return
* @throws IOException
*/
public boolean uploadByPath(String remotePath, String localPath) throws IOException {
boolean result = false;
// 进入被动模式
mFTPClient.enterLocalPassiveMode();
// 以二进制进行传输数据
mFTPClient.setFileType(FTP.BINARY_FILE_TYPE);
// 获取本地文件流
File localFile = new File(localPath);
if (!localFile.exists()) {
System.err.println("本地文件不存在");
return false;
}
//本地文件名
String fileName = localFile.getName();
// 如果远程文件包含 /
if (remotePath.contains(File.separator)) {
// 创建远程目录
boolean isCreateOk = createDirectory(remotePath, mFTPClient);
if (!isCreateOk) {
System.err.println("远程文件夹创建失败");
return false;
}
}
// 列出远程目录上的文件
FTPFile[] ftpFiles = mFTPClient.listFiles(remotePath);
long remoteSize = 0L;
// 定义上传后的文件名
String remoteFilePath = remotePath + File.separator + fileName;
// 如果目录下已经有文件
if (ftpFiles.length > 0) {
FTPFile mFtpFile = null;
for (FTPFile ftpFile : ftpFiles) {
if (ftpFile.getName().endsWith(fileName)) {
mFtpFile = ftpFile;
break;
}
}
// 远程存在同名文件
if (mFtpFile != null) {
if (!mFTPClient.deleteFile(remoteFilePath)) {
System.err.println("服务端删除文件操作失败");
}
}
}
// 获取本地文件流
FileInputStream fileInputStream = new FileInputStream(localFile);
// 开始上传
result = mFTPClient.storeFile(fileName, fileInputStream);
if (!result) {
return result;
}
return true;
}
/**
* 下载文件保存在本地
* @param remotePath 远程地址
* @param localDir 保存在本地的地址
* @return
* @throws IOException
*/
public boolean downLoad(String remotePath, String localDir) throws IOException {
// 进入被动模式 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据,ftp server可能每次开启不同的端口来传输数据,
// 但是在Linux上,由于安全限制,可能某些端口没有开启,所以就出现阻塞。
mFTPClient.enterLocalPassiveMode();
// 以二进制进行传输数据
mFTPClient.setFileType(FTP.BINARY_FILE_TYPE);
// 获取要下载的ftp文件名列表
FTPFile[] ftpFiles = mFTPClient.listFiles(remotePath);
if (ftpFiles == null || ftpFiles.length == 0) {
log.info("远程文件不存在");
return false;
} else if (ftpFiles.length > 1) {
log.info("远程文件是文件夹");
return false;
}
// 远程文件大小
long lRemoteSize = ftpFiles[0].getSize();
// 获取本地文件
File localFileDir = new File(localDir);
// 检查本地文件夹是否存在
if (!localFileDir.exists()) {
// 不存在就创建文件夹
localFileDir.mkdirs();
}
// 用本地文件夹 和 ftp文件名 构建一个本地File对象
File localFile = new File(localFileDir, ftpFiles[0].getName());
long localSize = 0;
FileOutputStream fos = null;
// 如果文件已经存在
if (localFile.exists()) {
// 比较本地文件大小 和 远程FTP文件大小 如果大小一样就代表已经下载完毕
if (localFile.length() == lRemoteSize) {
System.err.println("已经下载完毕");
return true;
} else if (localFile.length() < lRemoteSize) {
// 如果本地文件比远程FTP文件小 , 说明之前已经下载了
localSize = localFile.length();
// 要下载的文件存在,从本地文件大小的位置 进行断点续传 setRestartOffset: 断点续传方法
mFTPClient.setRestartOffset(localSize);
// 创建一个文件输出流以写入由指定的 File 对象表示的文件。如果第二个参数为真,则字节将写入文件的末尾而不是开头。创建一个新的 FileDescriptor 对象来表示此文件连接。
fos = new FileOutputStream(localFile, true);
}
}
if (fos == null) {
fos = new FileOutputStream(localFile);
}
// 检索文件流
InputStream is = mFTPClient.retrieveFileStream(remotePath);
byte[] buffers = new byte[1024 * 5];
long step = lRemoteSize / 10;
long process = localSize / step;
int len = -1;
while ((len = is.read(buffers)) != -1) {
// 二进制写入文件
fos.write(buffers, 0, len);
localSize += len;
long newProcess = localSize / step;
if (newProcess > process) {
process = newProcess;
System.err.println("下载进度:" + process);
}
}
is.close();
fos.close();
boolean isDo = mFTPClient.completePendingCommand();
return isDo;
}
/**
* 连接ftp服务器
* @return 是否连接成功
* @throws SocketException
* @throws IOException
*/
private boolean openConnection()
throws SocketException, IOException {
// ftp ip地址
String host = ftpConfig.getUrl();
// ftp 端口号
int port = ftpConfig.getPort();
// ftp 账号
String account = ftpConfig.getUsername();
// ftp 密码
String pwd = ftpConfig.getPassword();
mFTPClient.setControlEncoding("UTF-8");
// 连接FTP服务器,如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器
mFTPClient.connect(host, port);
// getReplyCode() 获取ftp应答 , FTPReply.isPositiveCompletion() 判断是否链接成功
if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
// 登录ftp服务器
mFTPClient.login(account, pwd);
// 判断是否链接成功
if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
// getSystemType() 从服务器获取系统类型并返回字符串 在第一次调用此方法后,该值在连接期间被缓存。换句话说,只有第一次调用此方法时,它才会向 FTP 服务器发出 SYST 命令。FTPClient 会记住该值并返回缓存的值,直到调用断开连接。
FTPClientConfig config = new FTPClientConfig(mFTPClient.getSystemType().split(" ")[0]);
config.setServerLanguageCode("zh");
// 设置连接超时时间
mFTPClient.setConnectTimeout(1000*30);
mFTPClient.configure(config);
return true;
}
}
// 断开ftp连接
disConnection();
return false;
}
/**
* 登出并断开连接
*/
public void logout() {
System.err.println("logout");
if (mFTPClient.isConnected()) {
System.err.println("logout");
try {
// 退出ftp
mFTPClient.logout();
disConnection();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 断开连接
*/
private void disConnection() {
if (mFTPClient.isConnected()) {
try {
mFTPClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 创建远程目录,切换目录
*
* @param remote 远程目录
* @param ftpClient ftp客户端
* @return 是否创建成功
* @throws IOException
*/
public boolean createDirectory(String remote, FTPClient ftpClient) throws IOException {
String dirctory = remote.substring(0, remote.lastIndexOf(File.separator) + 1);
if (!dirctory.equalsIgnoreCase(File.separator) && !ftpClient.changeWorkingDirectory(remote)) {
int start = 0;
int end = 0;
if (dirctory.startsWith(File.separator)) {
start = 1;
}
end = dirctory.indexOf(File.separator, start);
while (true) {
String subDirctory = remote.substring(start, end);
if (!ftpClient.changeWorkingDirectory(subDirctory)) {
if (ftpClient.makeDirectory(subDirctory)) {
ftpClient.changeWorkingDirectory(subDirctory);
} else {
System.err.println("创建目录失败");
return false;
}
}
start = end + 1;
end = dirctory.indexOf(File.separator, start);
if (end <= start) {
break;
}
}
}
ftpClient.changeWorkingDirectory(remote);
return true;
}
}
IFtpService
package com.vxdata.cases.modules.ftp.service;
public interface IFtpService {
/**
* 对外提供的上传文件方法
* @param fileName 要上传的文件名
* @param localPath 本地路径
* @param remotePath 远程路径
* @return
*/
boolean ftpUpload(String fileName, String localPath, String remotePath);
/**
* 对外提供的下载文件方法
* @param fileName 要下载的文件名
* @param localPath 本地存储的路径
* @param remotePath 远程路径
* @return
*/
boolean ftpDownload(String fileName, String localPath, String remotePath);
}
FtpServiceImpl
package com.vxdata.cases.modules.ftp.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.vxdata.cases.modules.file.exception.FastDFSException;
import com.vxdata.cases.modules.file.utils.FastDFSClient;
import com.vxdata.cases.modules.ftp.config.FtpConfig;
import com.vxdata.cases.modules.ftp.service.IFtpService;
import com.vxdata.cases.modules.ftp.utils.FtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.*;
@Service
@Slf4j
public class FtpServiceImpl implements IFtpService {
@Autowired
private FastDFSClient fastDFSClient;
@Autowired
FtpConfig ftpConfig;
/**
* 对外提供的上传文件方法
* @param fileName 要上传的文件名
* @param localPath 本地路径
* @param remotePath 远程路径
* @return
*/
@Override
public boolean ftpUpload(String fileName, String localPath, String remotePath) {
return FtpUtil.ftpUpload(fileName, localPath, remotePath);
}
/**
* 对外提供的下载文件方法
* @param fileName 要下载的文件名
* @param localPath 本地存储的路径
* @param remotePath 远程路径
* @return
*/
@Override
public boolean ftpDownload(String fileName, String localPath, String remotePath) {
return FtpUtil.ftpDownload(fileName, localPath, remotePath);
}
/**
* 将 InputStream 转成json对象
* @param inputStream
* @return
* @throws IOException
*/
public JSONObject inputStreamToString(InputStream inputStream) throws IOException{
// 将FTP的文件流 转成String格式内容
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
boolean firstLine = true;
String line = null; ;
while((line = bufferedReader.readLine()) != null){
if(!firstLine){
stringBuilder.append(System.getProperty("line.separator"));
}else{
firstLine = false;
}
stringBuilder.append(line);
}
JSONObject jsonObject = JSON.parseObject(stringBuilder.toString());
return jsonObject;
}
/**
* String 转 InputStream
* @param jsonStr
* @return
*/
public InputStream stringToInputStream(String jsonStr) {
InputStream inputStream = new ByteArrayInputStream(jsonStr.getBytes());
return inputStream;
}
/**
* JSONObject 转 InputStream
* @param jsonObject
* @return
*/
public InputStream objectToInputStream(JSONObject jsonObject) {
String str = jsonObject.toString();
InputStream inputStream = new ByteArrayInputStream(str.getBytes());
return inputStream;
}
/**
* 从fastDFS链接获取 InputStream
*/
public InputStream getImgInputStream(String filepath) {
InputStream inputStream = null;
try {
byte[] fileByte = fastDFSClient.download(filepath);
inputStream = new ByteArrayInputStream(fileByte);
} catch (FastDFSException e) {
e.printStackTrace();
}
return inputStream;
}
}