- 这里介绍
java
创建Ftp服务器
, 并且监测客户端上传文件后,读取文件的操作。使用hutool
创建Ftp客户端,使用ftplet-api
下的DefaultFtplet
进行文件监听。- 上传的文件夹通过不同用户添加,设置不同的用户名、密码、上传文件夹,测试时通过连接不同的用户,上传文件到指定文件夹。
- 这里有个小小的问题
DefaultFtplet
监听器是ftp
依赖下的类,测试使用Hutool
下的Ftp
类,所以无法通过hutool
的Ftp
类 测试指定文件夹上传,只能上传 创建Ftp 服务器
时 指定的文件夹,即创建Ftp服务器
时 添加不同用户
的文件夹
sql 数据表
CREATE TABLE `ftp_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ftp_code` varchar(255) NOT NULL COMMENT 'ftp编码',
`username` varchar(255) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`upload_folder` varchar(255) NOT NULL COMMENT '上传文件夹',
`status` int(11) NOT NULL DEFAULT '0' COMMENT '是否使用 1使用 0 未用',
PRIMARY KEY (`id`),
UNIQUE KEY `user` (`username`,`password`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
数据表实体类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FtpInfo {
private Integer id;
private String ftpCode;
private String username;
private String password;
private String uploadFolder;
private Integer status;
}
org.projectlombok
lombok
org.apache.ftpserver
ftpserver-core
1.2.0
org.apache.ftpserver
ftplet-api
1.2.0
org.apache.mina
mina-core
2.0.16
cn.hutool
hutool-all
5.8.18
这里提供一种,引入
ftp服务器
端口的方式,也可以通过方式引入
yml配置文件 定义账号 密码 端口号
ftp-server:
port: 27 # 端口号
配置类
package com.esframework.project.kdzt.ftp.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
/**
* @author Administrator
*/
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "ftp-server")
public class FtpProperties {
private Integer port;
}
配置监听器
package com.example.ftp.ftp2;
import org.apache.ftpserver.ftplet.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
/**
* ftp服务器侦听器
*
* @date 2023/06/29
*/
public class FtpServerListener extends DefaultFtplet {
public static final Logger log = LoggerFactory.getLogger(FtpServerListener.class);
/**
* 在连接* 开始连接@param session 会话
*
* @return {@link FtpletResult}
* @throws FtpException ftp例外
* @throws IOException ioexception
*/
@Override
public FtpletResult onConnect(FtpSession session) throws FtpException, IOException {
UUID sessionId = session.getSessionId();
if (sessionId != null) {
log.info("{}尝试登录ftpserver", sessionId);
}
User user = session.getUser();
if (user != null && user.getName() != null) {
assert sessionId != null;
log.info("{}尝试使用用户名:{},密码:{}登录ftpserver.", sessionId, user.getName(), user.getPassword());
}
return super.onConnect(session);
}
/**
* 在断开* 关闭连接@param session 会话
*
* @return {@link FtpletResult}
* @throws FtpException ftp例外
* @throws IOException ioexception
*/
@Override
public FtpletResult onDisconnect(FtpSession session) throws FtpException, IOException {
UUID sessionId = session.getSessionId();
User user = session.getUser();
if (sessionId != null) {
log.info("{}关闭ftpserver连接", sessionId);
}
if (user != null && user.getName() != null) {
assert sessionId != null;
log.info("{}用户名:{}关闭ftpserver连接.", sessionId, user.getName());
}
return super.onDisconnect(session);
}
/**
* 上传开始* 开始上传@param session 会话
*
* @param request 请求
* @return {@link FtpletResult}
* @throws FtpException ftp例外
* @throws IOException ioexception
*/
@Override
public FtpletResult onUploadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
//获取上传文件的上传路径
String path = session.getUser().getHomeDirectory();
//自动创建上传路径
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
//获取上传用户
String name = session.getUser().getName();
// 获取上传文件名
String filename = request.getArgument();
log.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{}',状态:开始上传~", name, path, filename);
return super.onUploadEnd(session, request);
}
/**
* 在上传* 上传完成@param session 会话
*
* @param request 请求
* @return {@link FtpletResult}
* @throws FtpException ftp例外
* @throws IOException ioexception
*/
@Override
public FtpletResult onUploadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
//获取上传文件的上传路径
String path = session.getUser().getHomeDirectory();
// 获取上传用户
String name = session.getUser().getName();
// 获取上传文件名
String fileName = request.getArgument();
File file = new File(path + "/" + fileName);
if (file.exists()) {
// 读取文件
String filePath = String.format("%s\\%s", path, fileName);
log.info("用户:'{}',上传文件到目录:'{}',文件名称为:'{},状态:成功!'", name, path, fileName);
}
return super.onUploadStart(session, request);
}
@Override
public FtpletResult onDownloadStart(FtpSession session, FtpRequest request) throws FtpException, IOException {
return super.onDownloadStart(session, request);
}
@Override
public FtpletResult onDownloadEnd(FtpSession session, FtpRequest request) throws FtpException, IOException {
return super.onDownloadEnd(session, request);
}
}
使用 ApplicationRunner
启动服务时,创建 ftp
服务
package com.example.ftp.ftp2;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ftp.SimpleFtpServer;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.List;
/**
* ftp服务器
*
* @author Administrator
* @date 2023/06/30
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class FtpServer implements ApplicationRunner {
private final FtpProperties ftpProperties;
@Override
public void run(ApplicationArguments args) throws Exception {
// 这里演示写了两个用户 实际业务中 可以查询数据库添加
BaseUser user = new BaseUser();
user.setName("user1");
user.setPassword("user1");
// 配置上传的文件夹 没有就新建
File folder = FileUtil.file("D:\\ftp1\\user1");
if(!folder.exists()) {
boolean mkdir = folder.mkdir();
}
user.setHomeDirectory("D:\\ftp1\\user1");
user.setMaxIdleTime(0);
user.setEnabled(true);
//设置用户权限 不设置权限 上传文件会被拒绝 导致上传失败
List authorities = Lists.newArrayList();
authorities.add(new WritePermission());
user.setAuthorities(authorities);
BaseUser user1 = new BaseUser();
user1.setName("user2");
user1.setPassword("user2");
// 配置上传的文件夹 没有就新建
File folder1 = FileUtil.file("D:\\ftp1\\user2");
if(!folder.exists()) {
boolean mkdir = folder1.mkdir();
}
user1.setHomeDirectory("D:\\ftp1\\user2");
user1.setMaxIdleTime(0);
user1.setEnabled(true);
//设置用户权限
List authorities1 = Lists.newArrayList();
authorities1.add(new WritePermission());
user1.setAuthorities(authorities1);
SimpleFtpServer simpleFtpServer = SimpleFtpServer.create()
// 添加用户
.addUser(user).addUser(user1)
.setPort(ftpProperties.getPort());
// 配置监听器
simpleFtpServer.addFtplet("folder", new FtpServerListener());
simpleFtpServer.start();
log.info("Ftp 开启=====》");
}
}
使用 hutool
进行测试
注意
下面使用 public boolean upload(String destPath, File file)
这个方法时 destPath
会根据 Ftp Server
创建时 user的 setHomeDirectory
参数变化,
setHomeDirectory
参数时,destPath
写全部路径,但是监听器无法获取 上传文件的全路径。setHomeDirectory
参数时, destPath
填写 /
就是 setHomeDirectory
定义的文件夹package com.example.ftp.ftp;
import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.ftp.Ftp;
import java.io.IOException;
public class FtpTest {
public static void main(String[] args) {
// 输入不同的用户名、密码 测试上传不通的文件夹
Ftp ftp1 = new Ftp("ip", port, "username", "password");
try {
ftp1.upload("/", FileUtil.file("D:\\FlFile\\1234.txt"));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ftp1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
hutool
工具类创建ftp服务器
, 使用 hutool
的 文件监听器 WatchMonitor 然后监听 指定目录,监听到新建文件,然后开始读取文件,但是如果项目中发生异常,监听会出问题。DefaultFtplet
, 可以监听到文件上传成功,然后操作。 BaseUser user1 = new BaseUser();
user1.setName("user2");
user1.setPassword("user2");
// 配置上传的文件夹 没有就新建
File folder1 = FileUtil.file("D:\\ftp1\\user2");
if(!folder.exists()) {
boolean mkdir = folder1.mkdir();
}
user1.setHomeDirectory("D:\\ftp1\\user2");
user1.setMaxIdleTime(0);
user1.setEnabled(true);
//设置用户权限
List authorities1 = Lists.newArrayList();
authorities1.add(new WritePermission());
user1.setAuthorities(authorities1);
ftp 文件监听,失败的迭代示例
package com.example.ftp.ftp;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharsetUtil;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.List;
@Component
public class FtpUpdate implements ApplicationRunner {
public void update() {
File file = FileUtil.file("D:\\ftp");
//这里只监听文件或目录的修改事件
WatchMonitor watchMonitor = WatchMonitor.create(file, WatchMonitor.ENTRY_CREATE);
watchMonitor.setWatcher(new Watcher() {
@Override
public void onCreate(WatchEvent> event, Path currentPath) {
Object obj = event.context();
Console.log("创建:{}-> {}", currentPath, obj);
String filePath = String.format("%s\\%s", currentPath, obj.toString());
String destPath = String.format("%s\\%s", "D:\\OfFile", obj);
// 读取文件
List readLines = FileUtil.readLines(destPath, CharsetUtil.UTF_8);
readLines.forEach(System.out::println);
}
@Override
public void onModify(WatchEvent> event, Path currentPath) {
Object obj = event.context();
Console.log("修改:{}-> {}", currentPath, obj);
}
@Override
public void onDelete(WatchEvent> event, Path currentPath) {
Object obj = event.context();
Console.log("删除:{}-> {}", currentPath, obj);
}
@Override
public void onOverflow(WatchEvent> event, Path currentPath) {
Object obj = event.context();
Console.log("Overflow:{}-> {}", currentPath, obj);
}
});
watchMonitor.setMaxDepth(3);
//设置监听目录的最大深入,目录层级大于制定层级的变更将不被监听,默认只监听当前层级目录
//启动监听
watchMonitor.start();
}
@Override
public void run(ApplicationArguments args) throws Exception {
update();
}
}