package com.aa.test;
import cn.hutool.core.util.StrUtil;
import com.nuctech.platform.tip.constant.TipConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @Description ftp操作工具类
*/
@Slf4j
public class FTPClientUtil {
/**
* Description: 从FTP服务器下载文件
* @param url FTP服务器hostname
* @param port FTP服务器端口
* @param username FTP登录账号
* @param password FTP登录密码
* @param remotePath FTP服务器上的相对路径
* @param fileName 要下载的文件名
* @param localPath 下载后保存到本地的路径
* @param menuName 使用menuname 来作为zip包的名称
* @return
*/
public static boolean downloadFile(String url, int port, String username, String password, List<String> remotePathes,
List<String> fileName, String localPath,String menuName)
{
boolean success = false;
FTPClient ftp = new FTPClient();
String zipPath=TipConstant.WORK_DIR+menuName+TipConstant.WORK_DIR_FIX;
ZipOutputStream zipOutputStream=null;
FileInputStream fileInputStream=null;
OutputStream is=null;
try
{
//创建zip包
zipOutputStream= new ZipOutputStream(new FileOutputStream(zipPath));
int reply;
// 连接FTP服务器
if (port > -1)
{
ftp.connect(url, port);
}
else
{
ftp.connect(url);
}
//登录
ftp.login(username, password);
// 设置连接超时时间,5000毫秒
ftp.setConnectTimeout(5000);
// 设置中文编码集,防止中文乱码
ftp.setControlEncoding("UTF-8");
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
log.info("下载ftp文件时,未连接到FTP,用户名或密码错误");
ftp.disconnect();
return success;
}else {
log.info("下载ftp文件时,FTP连接成功");
}
for(String remotePath:remotePathes){
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
ftp.changeWorkingDirectory(remotePath);//转移到FTP服务器目录
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs)
{
if (fileName.contains(ff.getName()))
{
File file = new File(localPath);
if(!file.exists()){
file.mkdirs();
}
File localFile = new File(localPath + "/" + ff.getName());
//开始从ftp下载图片
is = new FileOutputStream(localFile);
ftp.retrieveFile(ff.getName(), is);
is.close();
//创建条目
String name=menuName+File.separator+ff.getName();
ZipEntry zipEntry = new ZipEntry(name);
zipOutputStream.putNextEntry(zipEntry);
//获取文件资源
String absolutePath = localFile.getAbsolutePath();
fileInputStream = new FileInputStream(absolutePath);
//把文件写到zip包中
byte[] bytes = new byte[1024];
int read;
while ((read = fileInputStream.read(bytes)) != -1) {
zipOutputStream.write(bytes,0,read);
}
fileInputStream.close();
}
}
}
ftp.logout();
success = true;
}
catch (IOException e)
{
log.error("从FTP服务器下载文件异常:", e);
}
finally
{
if (ftp.isConnected())
{
try
{
is.close();
fileInputStream.close();
zipOutputStream.close();
ftp.disconnect();
}
catch (IOException e)
{
log.error("下载文件后关闭连接异常:", e);
}
}
}
return success;
}
/**
* Description: 向FTP服务器上传文件
* @param url FTP服务器hostname
* @param port FTP服务器端口,如果默认端口请写-1
* @param username FTP登录账号
* @param password FTP登录密码
* @param path FTP服务器保存目录
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadFile(String url, int port, String username, String password, String path,
String filename, InputStream input)
{
boolean success = false;
FTPClient ftp = new FTPClient();
try
{
int reply;
// 连接FTP服务器
if (port > -1)
{
ftp.connect(url, port);
}
else
{
ftp.connect(url);
}
// 登录FTP
ftp.login(username, password);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftp.disconnect();
return success;
}
//切换工作路径
List<String> pathList = getPathList(path);
for(int i=0;i<pathList.size();i++){
String dir = new String(pathList.get(i).getBytes("GBK"),"iso-8859-1");
if(!ftp.changeWorkingDirectory(dir)){
if(!ftp.makeDirectory(dir)){
log.error("创建文件夹失败");
throw new IOException("创建文件夹失败");
}
ftp.changeWorkingDirectory(dir);
}
}
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.storeFile(filename, input);
input.close();
ftp.logout();
success = true;
}
catch (IOException e)
{
success = false;
log.error("从FTP服务器上传文件异常", e);
}
finally
{
if (ftp.isConnected())
{
try
{
ftp.disconnect();
}
catch (IOException e)
{
log.error("上传文件后关闭连接异常", e);
}
}
}
return success;
}
/**
* Description: 向FTP服务器上传xml文件
* @param url FTP服务器hostname
* @param port FTP服务器端口,如果默认端口请写-1
* @param username FTP登录账号
* @param password FTP登录密码
* @param path FTP服务器保存目录
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
*/
public static boolean uploadXmlFile(String url, int port, String username, String password, String path,
String filename, InputStream input)
{
boolean success = false;
FTPClient ftp = new FTPClient();
try
{
int reply;
// 连接FTP服务器
if (port > -1)
{
ftp.connect(url, port);
}
else
{
ftp.connect(url);
}
// 登录FTP
ftp.login(username, password);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftp.disconnect();
return success;
}
String dir = new String(path.getBytes("GBK"),"iso-8859-1");
if(!ftp.changeWorkingDirectory(dir)){
if(!ftp.makeDirectory(dir)){
log.error("创建文件夹失败");
throw new IOException("创建文件夹失败");
}
ftp.changeWorkingDirectory(dir);
}
ftp.setFileType(FTP.BINARY_FILE_TYPE);
ftp.storeFile(filename, input);
input.close();
ftp.logout();
success = true;
}
catch (IOException e)
{
success = false;
log.error("从FTP服务器上传文件异常", e);
}
finally
{
if (ftp.isConnected())
{
try
{
ftp.disconnect();
}
catch (IOException e)
{
log.error("上传文件后关闭连接异常", e);
}
}
}
return success;
}
/**
* <删除FTP上的文件>
* <远程删除FTP服务器上的录音文件>
* @param url FTP服务器IP地址
* @param port FTP服务器端口
* @param username FTP服务器登录名
* @param password FTP服务器密码
* @param remotePath 远程文件路径
* @param fileName 待删除的文件名
* @return
* @see [类、类#方法、类#成员]
*/
public static boolean deleteFtpFile(String url, int port, String username, String password, String remotePath,
String fileName)
{
boolean success = false;
FTPClient ftp = new FTPClient();
try
{
int reply;
// 连接FTP服务器
if (port > -1)
{
ftp.connect(url, port);
}
else
{
ftp.connect(url);
}
// 登录
ftp.login(username, password);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftp.disconnect();
return success;
}
// 转移到FTP服务器目录
//删除空的文件夹
String path = remotePath.substring(0, remotePath.lastIndexOf(File.separator));
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
fileName=new String(fileName.getBytes("GBK"),"iso-8859-1");
if(StrUtil.isBlank(fileName)){
success=ftp.deleteFile(remotePath);
}else{
ftp.changeWorkingDirectory(remotePath);
//获取当前目录下所有得子文件名称,进行删除
FTPFile[] ftpFiles = ftp.listFiles();
for (FTPFile ftpFile : ftpFiles) {
boolean isDel=ftp.deleteFile(remotePath + File.separator + ftpFile.getName());
if(!isDel){
throw new IOException("删除子文件失败");
}
}
FTPFile[] ftpFiles1 = ftp.listFiles();
if(null!=ftpFiles1&&ftpFiles1.length!=0){
}else{
ftp.changeWorkingDirectory(path);
ftp.removeDirectory(remotePath);
}
success=true;
}
ftp.logout();
}
catch (IOException e)
{
log.error("FTP服务器上删除文件异常", e);
success = false;
}
finally
{
if (ftp.isConnected())
{
try
{
ftp.disconnect();
}
catch (IOException e)
{
log.error("FTP服务器上删除文件后关闭连接", e);
}
}
}
return success;
}
/**
* <删除FTP上的文件>
* <远程删除FTP服务器上的录音文件>
* @param url FTP服务器IP地址
* @param port FTP服务器端口
* @param username FTP服务器登录名
* @param password FTP服务器密码
* @param remotePath 远程文件路径
* @return
* @see [类、类#方法、类#成员]
*/
public static boolean delDirPath(String url, int port, String username, String password, String remotePath
) {
boolean success = false;
FTPClient ftp = new FTPClient();
try
{
int reply;
// 连接FTP服务器
if (port > -1)
{
ftp.connect(url, port);
}
else
{
ftp.connect(url);
}
// 登录
ftp.login(username, password);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftp.disconnect();
return success;
}
String path = remotePath.substring(0, remotePath.lastIndexOf(File.separator));
remotePath=new String(remotePath.getBytes("GBK"),"iso-8859-1");
ftp.changeWorkingDirectory(remotePath);
FTPFile[] ftpFiles = ftp.listFiles();
if(null!=ftpFiles&&ftpFiles.length!=0){
}else{
ftp.changeWorkingDirectory(path);
ftp.removeDirectory(remotePath);
}
ftp.logout();
}
catch (IOException e)
{
log.error("FTP服务器上删除文件异常", e);
success = false;
}
finally
{
if (ftp.isConnected())
{
try
{
ftp.disconnect();
}
catch (IOException e)
{
log.error("FTP服务器上删除文件后关闭连接", e);
}
}
}
return success;
}
/**
* <删除FTP上的文件>
* <远程删除FTP服务器上的录音文件>
* @param url FTP服务器IP地址
* @param port FTP服务器端口
* @param username FTP服务器登录名
* @param password FTP服务器密码
* @param remotePath 远程文件路径
* @param fileName 待删除的文件名
* @return
* @see [类、类#方法、类#成员]
*/
public static boolean delXmlFile(String url, int port, String username, String password, String remotePath,
String fileName)
{
boolean success = false;
FTPClient ftp = new FTPClient();
try
{
int reply;
// 连接FTP服务器
if (port > -1)
{
ftp.connect(url, port);
}
else
{
ftp.connect(url);
}
// 登录
ftp.login(username, password);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply))
{
ftp.disconnect();
return success;
}
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
fileName=new String(fileName.getBytes("GBK"),"iso-8859-1");
//切换工作目录
ftp.changeWorkingDirectory(remotePath);
//获取当前目录下所有得子文件名称,进行删除
boolean isDel=ftp.deleteFile(remotePath + File.separator + fileName);
if(!isDel){
throw new IOException("删除子文件失败");
}
success=true;
ftp.logout();
}
catch (IOException e)
{
log.error("FTP服务器上删除文件异常", e);
success = false;
}
finally
{
if (ftp.isConnected())
{
try
{
ftp.disconnect();
}
catch (IOException e)
{
log.error("FTP服务器上删除文件后关闭连接", e);
}
}
}
return success;
}
//分割路径
public static List<String> getPathList(String path){
String[] dirs = path.split("/");
List<String> list = new ArrayList<>();
String pathname = "";
for(String str : dirs){
if(StrUtil.isEmpty(str)){
continue;
}
pathname = pathname + "/" + str;
list.add(pathname);
}
return list;
}
}
工具类,一找一大把,那就不多说了。我主要说说遇到的问题:连接ftp的客户端,我没有和ftp服务器部署在同一个电脑,如果是同一个电脑,应该也没啥问题。我发现,在我拿到流数据,往ftp文件夹下写图片数据的时候,没有把图片写进去。通过一行一行代码打印日志,排查发现
boolean storeFile = ftp.storeFile(filename, input);方法没有拿到返回值,没有反应。
原因是:
FTP协议有两种工作方式:PORT方式和PASV方式,中文意思为主动式和被动式。
PORT(主动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。当需要传送数据时,客户端在命令链路上用PORT 命令告诉服务器:“我打开了XXXX端口,你过来连接我”。于是服务器从20端口向客户端的 XXXX端口发送连接请求,建立一条数据链路来传送数据。
PASV(被动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请 求,服务器接受连接,建立一条命令链路。当需要传送数据时,服务器在命令链路上用PASV 命令告诉客户端:“我打开了XXXX端口,你过来连接我”。于是客户端向服务器的XXXX端口 发送连接请求,建立一条数据链路来传送数据。
解决办法:ftpclient.enterLocalPassiveMode();
/**
* Description: 向FTP服务器上传文件
*
* @param url FTP服务器hostname
* @param port FTP服务器端口,如果默认端口请写-1
* @param username FTP登录账号
* @param password FTP登录密码
* @param path FTP服务器保存目录
* @param filename 上传到FTP服务器上的文件名
* @param input 输入流
* @return 成功返回true,否则返回false
**/
public static boolean uploadFile(String url, int port, String username, String password, String path,
String filename, InputStream input) {
log.info("开始调用uploadfile方法");
boolean success = false;
FTPClient ftp = new FTPClient();
try {
int reply;
// 连接FTP服务器
if (port > -1) {
log.info("开始调用uploadfile方法,url:"+url+",port:"+port);
ftp.connect(url, port);
} else {
log.info("开始调用uploadfile方法else,url:"+url+",port:"+port);
ftp.connect(url);
}
// 登录FTP
boolean login = ftp.login(username, password);
log.info("开始调用uploadfile方法,登录FTP,login:"+login);
reply = ftp.getReplyCode();
log.info("开始调用uploadfile方法,登录FTP,reply:"+reply);
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
log.info("开始调用uploadfile方法==ftp.disconnect()");
return success;
}
String[] ftpPathDir = path.split("/");
//切换工作路径
List<String> pathList = Arrays.asList(ftpPathDir);
for (int i = 0; i < pathList.size(); i++) {
String dir = new String(pathList.get(i).getBytes("GBK"), "iso-8859-1");
if (!ftp.changeWorkingDirectory(dir)) {
if (!ftp.makeDirectory(dir)) {
log.error("创建文件夹失败");
}
ftp.changeWorkingDirectory(dir);
}
}
boolean setFileType = ftp.setFileType(FTP.BINARY_FILE_TYPE);
log.info("开始调用uploadfile方法==setFileType:"+setFileType);
ftp.enterLocalPassiveMode();
boolean storeFile = ftp.storeFile(filename, input);
log.info("开始调用uploadfile方法==storeFile:"+storeFile);
input.close();
boolean logout = ftp.logout();
log.info("开始调用uploadfile方法==logout:"+logout);
success = true;
} catch (IOException e) {
success = false;
log.error("从FTP服务器上传文件异常", e);
} finally {
log.error("finally从FTP服务器上传文件异常");
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException e) {
log.error("上传文件后关闭连接异常", e);
}
}
}
log.info("调用uploadfile方法结束,结果="+success);
return success;
}