注:文章皆为个人纪录,可用性请以最终结果为准,若有错还请大佬们指出,谢谢!
温馨提示:(默认权限下,暂不支持往本地的C盘中写入文件)
一、引入依赖
<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
二、获取配置信息 FTPConfig
2.1 添加yml配置
system:
ftp:
# ftp 文件服务器配置信息
userName: xxx
password: xxx
host: xxx
port: 21 # 默认
2.2 获取配置
package com.jxz.owner.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Description: FTP 文件服务器配置
*
* @Date: 2022/2/14
* @Author: jiangXueZhi
*/
@Data
@Component
@ConfigurationProperties(prefix = "system.ftp")
public class FTPConfig {
private String userName;
private String password;
private String host;
private int port;
}
三、程序入口
/**
* FTP 的Java客户端操作(暂不支持在本地的C盘中写入文件)
*/
@RequestMapping(value = "/ftpFileHandle", method = RequestMethod.GET)
public void ftpFileHandle() {
/* FTP 基操 */
// FTPClient ftp = FTPUtils.getFTPClient(ftpConfig.getHost(), ftpConfig.getPort(), ftpConfig.getUserName(), ftpConfig.getPassword()); // 获取 ftp客户端
// FTPUtils.downLoadFTP(ftp, "/report", "测试文件.txt", "D:\\");
// FTPUtils.copyFile(ftp, "/report", "/report/test", "jxz.txt");
// FTPUtils.uploadFile(ftp, "D:\\测试文件3.txt", "/report");
// FTPUtils.moveFile(ftp, "/report/test", "/report/test2");
// FTPUtils.deleteByFolder(ftp, "/report/test");
// System.out.println(FTPUtils.readFileByFolder(ftp, "/report/test2"));
// FTPUtils.closeFTP(ftp);
// System.exit(0);
/* FTP 上传与下载的快捷操作 */
// FTPUtils.uploadFile("hello shenzhen,你好 深圳", ftpConfig.getUserName(), ftpConfig.getPassword(), ftpConfig.getHost(), ftpConfig.getPort(), "测试文件.txt", "/report");
// FTPUtils.downLoadFile(ftpConfig.getUserName(), ftpConfig.getPassword(), ftpConfig.getHost(), ftpConfig.getPort(), "测试文件.txt", "/report", "D:\\");
}
四、工具类 FTPUtils
package com.paycool.inpay.core.utils;
import lombok.Data;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @Description: JAVA 操作 FTP 文件服务器的工具类
*
* @Date: 2022/2/14
* @Author: jiangXueZhi
*/
public class FTPUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(FTPUtils.class);
/**
* 获取FTPClient对象
*
* @param ftpHost 服务器IP
* @param ftpPort 服务器端口号
* @param ftpUserName 用户名
* @param ftpPassword 密码
* @return FTPClient
*/
public static FTPClient getFTPClient(String ftpHost, int ftpPort, String ftpUserName, String ftpPassword) {
FTPClient ftp = null;
try {
ftp = new FTPClient();
// 连接FPT服务器,设置IP及端口
ftp.connect(ftpHost, ftpPort);
// 设置用户名和密码
ftp.login(ftpUserName, ftpPassword);
// 设置连接超时时间,5000毫秒
ftp.setConnectTimeout(5000);
// 设置中文编码集,防止中文乱码
ftp.setControlEncoding("UTF-8");
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
LOGGER.info("未连接到FTP,用户名或密码错误");
ftp.disconnect();
} else {
LOGGER.info("FTP连接成功");
}
} catch (SocketException e) {
e.printStackTrace();
LOGGER.info("FTP的IP地址可能错误,请正确配置");
} catch (IOException e) {
e.printStackTrace();
LOGGER.info("FTP的端口错误,请正确配置");
}
return ftp;
}
/**
* 关闭FTP
*
* @param ftp ftp客户端
* @return 关闭
*/
public static boolean closeFTP(FTPClient ftp) {
try {
ftp.logout();
} catch (Exception e) {
LOGGER.error("FTP关闭失败");
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
LOGGER.error("FTP关闭失败");
}
}
}
return false;
}
/* ---------------------------- FTP 基操 start ---------------------------- */
/**
* 下载FTP下指定文件
*
* @param ftp ftp客户端
* @param filePath FTP文件路径
* @param fileName 文件名
* @param downPath 下载保存的目录
* @param deleteLocalFile true:删除本地文件
* @return FTP下指定文件
*/
public static boolean downLoadFTP(FTPClient ftp, String filePath, String fileName, String downPath, boolean deleteLocalFile) {
// 默认失败
boolean flag = false;
try {
// 跳转到文件目录
ftp.changeWorkingDirectory(filePath);
// 获取目录下文件集合
ftp.enterLocalPassiveMode();
FTPFile[] files = ftp.listFiles();
for (FTPFile file : files) {
// 取得指定文件并下载
if (file.getName().equals(fileName)) {
File downFile = new File(downPath + "/" + file.getName());
OutputStream out = new FileOutputStream(downFile);
// 绑定输出流下载文件,需要设置编码集,不然可能出现文件为空的情况
flag = ftp.retrieveFile(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1), out);
// 下载成功后删除文件
if (deleteLocalFile) {
ftp.deleteFile(new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
}
out.flush();
out.close();
if (flag) {
LOGGER.info("下载成功");
} else {
LOGGER.error("下载失败");
}
}
}
} catch (Exception e) {
LOGGER.error("下载失败");
}
return flag;
}
/**
* FTP文件上传
*
* @param ftp ftp客户端
* @param filePath 源文件夹地址
* @param ftpPath 目标文件夹地址
* @return 上传成功与否
*/
public static boolean uploadFile(FTPClient ftp, String filePath, String ftpPath) {
boolean flag = false;
InputStream in = null;
try {
// 设置PassiveMode传输
ftp.enterLocalPassiveMode();
// 设置二进制传输,使用BINARY_FILE_TYPE,ASC容易造成文件损坏
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
// 判断FPT目标文件夹是否存在 不存在则创建
if (!ftp.changeWorkingDirectory(ftpPath)) {
ftp.makeDirectory(ftpPath);
}
// 跳转目标目录
ftp.changeWorkingDirectory(ftpPath);
// 上传文件
File file = new File(filePath);
in = new FileInputStream(file);
String tempName = ftpPath + "/" + file.getName();
flag = ftp.storeFile(new String(tempName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1), in);
if (flag) {
LOGGER.info("上传成功");
} else {
LOGGER.error("上传失败");
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("上传失败");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
/**
* FPT上文件的复制
*
* @param ftp ftp客户端
* @param olePath 原文件地址
* @param newPath 新保存地址
* @param fileName 文件名
* @return 复制成功与否
*/
public static boolean copyFile(FTPClient ftp, String olePath, String newPath, String fileName) {
boolean flag = false;
try {
// 跳转到文件目录
ftp.changeWorkingDirectory(olePath);
// 设置连接模式,不设置会获取为空
ftp.enterLocalPassiveMode();
// 获取目录下文件集合
FTPFile[] files = ftp.listFiles();
ByteArrayInputStream in;
ByteArrayOutputStream out;
for (FTPFile file : files) {
// 取得指定文件并下载
if (file.getName().equals(fileName)) {
// 读取文件,使用下载文件的方法把文件写入内存,绑定到out流上
out = new ByteArrayOutputStream();
ftp.retrieveFile(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1), out);
in = new ByteArrayInputStream(out.toByteArray());
// 创建新目录
ftp.makeDirectory(newPath);
// 文件复制,先读,再写
// 二进制
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
flag = ftp.storeFile(newPath + "/" + (new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)), in);
out.flush();
out.close();
in.close();
if (flag) {
LOGGER.info("转存成功");
} else {
LOGGER.error("复制失败");
}
}
}
} catch (Exception e) {
LOGGER.error("复制失败");
}
return flag;
}
/**
* 实现文件夹的移动,这里做的是一个文件夹下的所有内容移动到新的文件,
* 如果要做指定文件移动,加个判断判断文件名
* 如果不需要移动,只是需要文件重命名,可以使用ftp.rename(oleName,newName)
*
* @param ftp ftp 客户端
* @param oldPath 文件源地址
* @param newPath 文件目标地址
* @return 移动成功与否
*/
public static boolean moveDirectory(FTPClient ftp, String oldPath, String newPath) {
boolean flag = false;
try {
ftp.changeWorkingDirectory(oldPath);
ftp.enterLocalPassiveMode();
// 获取文件数组
FTPFile[] files = ftp.listFiles();
// 新文件夹不存在则创建
if (!ftp.changeWorkingDirectory(newPath)) {
ftp.makeDirectory(newPath);
}
//回到原有工作目录
ftp.changeWorkingDirectory(oldPath);
for (FTPFile file : files) {
// 转存目录
flag = ftp.rename(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1),
newPath + "/" + new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
if (flag) {
LOGGER.info(file.getName() + "移动成功");
} else {
LOGGER.error(file.getName() + "移动失败");
}
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("移动文件失败");
}
return flag;
}
/**
* 移动文件
*
* @param ftp xx
* @param oldPath xx
* @param newPath xx
* @return xx
*/
public static boolean moveFile(FTPClient ftp, String oldPath, String newPath) {
boolean flag = false;
try {
ftp.changeWorkingDirectory(oldPath);
ftp.enterLocalPassiveMode();
// 获取文件数组
FTPFile[] files = ftp.listFiles();
// 新文件夹不存在则创建
if (!ftp.changeWorkingDirectory(newPath)) {
ftp.makeDirectory(newPath);
}
//回到原有工作目录
ftp.changeWorkingDirectory(oldPath);
for (FTPFile file : files) {
if (file.isFile()) {
// 转存目录
flag = ftp.rename(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1),
newPath + "/" + new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
if (flag) {
LOGGER.info(file.getName() + "移动成功");
} else {
LOGGER.error(file.getName() + "移动失败");
}
}
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("移动文件失败");
}
return flag;
}
/**
* 删除FTP上指定文件夹下文件及其子文件方法,添加了对中文目录的支持
*
* @param ftp ftp 客户端
* @param ftpFolder 需要删除的文件夹
* @return 删除成功与否
*/
public static boolean deleteByFolder(FTPClient ftp, String ftpFolder) {
boolean flag = false;
try {
ftp.changeWorkingDirectory(new String(ftpFolder.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
ftp.enterLocalPassiveMode();
FTPFile[] files = ftp.listFiles();
for (FTPFile file : files) {
// 判断为文件则删除
if (file.isFile()) {
ftp.deleteFile(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
}
// 判断是文件夹
if (file.isDirectory()) {
String childPath = ftpFolder + "/" + file.getName();
// 递归删除子文件夹
deleteByFolder(ftp, childPath);
}
}
// 循环完成后删除文件夹
flag = ftp.removeDirectory(new String(ftpFolder.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
if (flag) {
LOGGER.info(ftpFolder + "文件夹删除成功");
} else {
LOGGER.error(ftpFolder + "文件夹删除成功");
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("删除失败");
}
return flag;
}
/**
* 遍历解析文件夹下所有文件
*
* @param folderPath 需要解析的的文件夹
* @param ftp ftp客户端
* @return 遍历文件的内容
*/
public static List<Map<String, Object>> readFileByFolder(FTPClient ftp, String folderPath) {
try {
ftp.changeWorkingDirectory(new String(folderPath.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
// 设置FTP连接模式
ftp.enterLocalPassiveMode();
// 获取指定目录下文件文件对象集合
FTPFile[] files = ftp.listFiles();
InputStream in;
BufferedReader reader;
List<Map<String, Object>> fileContentList = new LinkedList<>(); // 文件内容集合
for (FTPFile file : files) {
// 判断为txt文件则解析
if (file.isFile()) {
String fileName = file.getName();
if (fileName.endsWith(".txt")) {
in = ftp.retrieveFileStream(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
String temp;
StringBuilder buffer = new StringBuilder();
while ((temp = reader.readLine()) != null) {
buffer.append(temp);
}
reader.close();
in.close();
// ftp.retrieveFileStream使用了流,需要释放一下,不然会返回空指针
ftp.completePendingCommand();
// 这里就把一个txt文件完整解析成了个字符串,就可以调用实际需要操作的方法
Map<String, Object> map = new HashMap<>();
map.put("fileName", fileName);
map.put("fileContent", buffer.toString());
fileContentList.add(map);
}
}
// 判断为文件夹,递归
if (file.isDirectory()) {
String path = folderPath + "/" + file.getName();
readFileByFolder(ftp, path);
}
}
return fileContentList;
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("文件解析失败");
}
return null;
}
/**
* 获取文件夹下的单个文件
*
* @param ftp ftp客户端
* @param folderPath 需要解析的的文件夹,其中有且仅有一个文件
* @param destination 文件写入的本地地址
* @return 流 + fileName
*/
public static File readSingleFileByFolder(FTPClient ftp, String folderPath, String destination) {
InputStream in = null;
String fileName = null;
try {
ftp.changeWorkingDirectory(new String(folderPath.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
// 设置FTP连接模式
ftp.enterLocalPassiveMode();
// 获取指定目录下文件文件对象集合
FTPFile[] files = ftp.listFiles();
for (FTPFile file : files) {
// 判断为文件则解析
if (file.isFile()) {
in = ftp.retrieveFileStream(new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
fileName = file.getName();
// ftp.retrieveFileStream使用了流,需要释放一下,不然会返回空指针
ftp.completePendingCommand();
break; // 目录中有且仅有一个文件
}
}
if (in != null) {
return FileUtils.getFile(destination + fileName, in);
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("文件解析失败");
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Data
public static class ReadSingleFileResult{
private InputStream inputStream;
private String fileName;
}
/* ---------------------------- FTP 基操 end ----------------------------*/
/* ---------------------------- FTP 上传与下载的快捷操作 start ----------------------------*/
/**
* 向ftp写文件(数据)
*
* @param fileContent 要写入的文件内容
* @param userName ftp登录用户名
* @param password ftp登录密码
* @param host ftp的IP地址
* @param port ftp端口号
* @param fileName 创建的文件
* @param ftpPath 指定写入FTP的目录
*/
public static void uploadFile(String fileContent, String userName, String password, String host, int port, String fileName, String ftpPath) {
FTPClient ftpClient = new FTPClient();
try {
InputStream in;
// 1.输入流
in = new ByteArrayInputStream(fileContent.getBytes());
// 2.连接服务器
ftpClient.connect(host, port);
// 3.登录ftp
ftpClient.login(userName, password);
// 4.指定写入Ftp的目录
ftpClient.changeWorkingDirectory(ftpPath);
// 5.写操作
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ftpClient.storeFile(new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1), in);
in.close();
LOGGER.info("已上传");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* ftp下载数据
*
* @param userName ftp登录用户名
* @param userPassword ftp登录密码
* @param host ftp的IP地址
* @param port ftp的端口号
* @param fileName 创建的文件
* @param ftpPath 指定FTP的目录
* @param localPath 指定本地写入文件的地址
*/
public static void downLoadFile(String userName, String userPassword, String host, int port, String fileName, String ftpPath, String localPath) {
FTPClient ftp = new FTPClient();
try {
int reply;
//1.连接服务器
ftp.connect(host, port);
//2.登录服务器 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
ftp.login(userName, userPassword);
//3.判断登陆是否成功
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
LOGGER.info("未连接到FTP,用户名或密码错误");
ftp.disconnect();
} else {
LOGGER.info("FTP连接成功");
}
//4.指定要下载的目录
ftp.changeWorkingDirectory(ftpPath); // 转移到FTP服务器目录
//5.遍历下载的目录
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
//解决中文乱码问题,两次解码
byte[] bytes = ff.getName().getBytes(StandardCharsets.ISO_8859_1);
String fn = new String(bytes, StandardCharsets.UTF_8);
if (fn.equals(fileName)) {
//6.写操作,将其写入到本地文件中
File localFile = new File(localPath + ff.getName());
OutputStream is = new FileOutputStream(localFile);
ftp.retrieveFile(ff.getName(), is);
is.close();
}
}
ftp.logout();
LOGGER.info("已下载");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/* ---------------------------- FTP 上传与下载的快捷操作 end ----------------------------*/
}
五、下载 FTP 客户端 FileZilla 进行测试
5.1 下载地址:
FileZilla - The free FTP solution
5.2 下载安装步骤:
A. B.
C. D. 安装无脑下一步,注意创建桌面 icon 即可