来一个复古的技术FTP

背景

       10年前的老代码,需要升级springboot框架,在升级过程中,测试业务流程里,有FTP的下载业务,不管测试环境如何测试,都没有成功,最后只能自己搭建一个FTP服务器,写一个ftp-demo来测试。记录一下过程,防止后续使用的时候在来一次。

CentOS7安装FTP

步骤1:安装 vsftpd

# 更新系统软件包
sudo yum update -y# 安装 vsftpd
sudo yum install vsftpd -y

步骤2:启动服务并设置开机自启

# 启动 vsftpd 服务
sudo systemctl start vsftpd# 设置开机自启
sudo systemctl enable vsftpd# 检查服务状态
sudo systemctl status vsftpd# 停止服务
sudo systemctl stop vsftpd# 重启服务
sudo systemctl restart vsftpd

步骤3:配置防火墙

# 开放FTP端口(21和被动模式端口范围)
sudo firewall-cmd --zone=public --add-port=21/tcp --permanent
sudo firewall-cmd --zone=public --add-port=30000-31000/tcp --permanent
sudo firewall-cmd --zone=public --add-service=ftp --permanent# 重新加载防火墙规则
sudo firewall-cmd --reload

步骤4:配置 vsftpd

cd /etc/vsftpd/cp vsftpd.conf vsftpd.conf_default
修改下列参数的值
anonymous_enable=NO          #禁止匿名登录FTP服务器
local_enable=YES             #允许本地用户登录FTP服务器
listen=YES                   #监听IPv4 sockets
#listen_ipv6=YES             #关闭监听IPv6 sockets或者改为NO
chroot_local_user=YES        #全部用户被限制在主目录
chroot_list_enable=YES       #启用例外用户名单
chroot_list_file=/etc/vsftpd/chroot_list  #指定例外用户列表文件,列表中用户不被锁定在主目录
allow_writeable_chroot=YES
pasv_enable=YES
pasv_min_port=30000
pasv_max_port=31000
以上配置可以直接用下面命令进行替换修改(一句一句执行)
sed -i 's/anonymous_enable=YES/anonymous_enable=NO/' /etc/vsftpd/vsftpd.conf
sed -i 's/listen=NO/listen=YES/' /etc/vsftpd/vsftpd.conf
sed -i 's/listen_ipv6=YES/listen_ipv6=NO/' /etc/vsftpd/vsftpd.conf
sed -i 's/#chroot_local_user=YES/chroot_local_user=YES/' /etc/vsftpd/vsftpd.conf
sed -i 's/#chroot_list_enable=YES/chroot_list_enable=YES/' /etc/vsftpd/vsftpd.conf
sed -i 's/#chroot_list_file=/chroot_list_file=/' /etc/vsftpd/vsftpd.conf
echo "allow_writeable_chroot=YES" >> /etc/vsftpd/vsftpd.conf
# 被动模式
echo "pasv_enable=YES">> /etc/vsftpd/vsftpd.conf
echo "pasv_min_port=30001">> /etc/vsftpd/vsftpd.conf
echo "pasv_max_port=30010">> /etc/vsftpd/vsftpd.conf

 步骤5:创建FTP用户

# 创建用户(例如用户名为 ftpuser,目录为 /home/ftpuser)
sudo useradd -m -d /home/ftpuser -s /sbin/nologin ftpuser# 设置用户密码
sudo passwd ftpuser# 确保用户目录权限正确
sudo chmod -R 750 /home/ftpuser
sudo chown -R ftpuser: /home/ftpuser例如:
sudo useradd -m -d /home/douzi -s /sbin/nologin douzi
sudo passwd 123456
sudo chmod -R 750 /home/douzi
sudo chown -R douzi: /home/douzi

步骤6:重启vsftpd服务

# 启动 vsftpd 服务
sudo systemctl restart vsftpd

步骤7:客户端登录测试

ftp localhost
问题1:ftp客户端未安装

解决办法:
sudo yum install ftp
问题2:登录失败

找的截图,不要纠结里边的命令,只看红框部分即可

 解决办法:
vi /etc/pam.d/vsftpd# 注释以下一行
#auth       required    pam_shells.so

重启vsftpd服务,再进行登录提示:

再手动在/etc/vsftpd/目录下创建一下chroot_list文件即可

cd /etc/vsftpd/touch /etc/vsftpd/chroot_list

 然后重启vsftpd服务,登录即可正常:

登陆后默认为二进制传输模式

扩展FTP客户端命令:

dir ls cd pwd lcd(切换工作目录)  mkdir rmdir get mget(下载多个文件) put mput(上传多个文件) rename delete mdelete(删除多个ftp文件) rmdir ascii,bin(切换传输模式) close(关闭链接) open(重连ftp) quit

扩展防火墙命令:

一、防火墙的开启、关闭、禁用命令
设置开机启用防火墙:systemctl enable firewalld
设置开机禁用防火墙:systemctl disable firewalld
启动防火墙:       systemctl start firewalld
关闭防火墙:       systemctl stop firewalld 或 systemctl stop firewalld.service
检查防火墙状态     systemctl status firewalld二、使用firewall-cmd配置端口
查看防火墙状态: firewall-cmd --state
重新加载配置:   firewall-cmd --reload
查看开放的端口: firewall-cmd --list-ports
开启防火墙端口: firewall-cmd --zone=public --add-port=9200/tcp --permanent

扩展FTP配置项说明:

1. 基础访问控制
配置项默认值说明
anonymous_enableNO是否允许匿名登录(YES/NO
local_enableYES是否允许本地用户登录(YES/NO
write_enableYES是否允许写入操作(上传/删除/重命名)
2. 权限与安全
配置项默认值说明
local_umask022本地用户创建文件的权限掩码(022表示文件权限为644,目录为755
chroot_local_userNO是否将本地用户限制在其主目录(需配合allow_writeable_chroot=YES使用)
allow_writeable_chrootNO允许被chroot的用户目录可写(需chroot_local_user=YES
3. 连接与日志
配置项默认值说明
dirmessage_enableYES显示目录欢迎消息(消息文件默认为.message
xferlog_enableYES启用传输日志(记录上传/下载)
xferlog_file/var/log/vsftpd.log指定日志文件路径
xferlog_std_formatYES使用标准FTP日志格式(兼容wu-ftp格式)
connect_from_port_20YES主动模式时,强制数据连接从端口20发起
4. 超时设置
配置项默认值说明
idle_session_timeout600空闲会话超时时间(秒)
data_connection_timeout120数据连接超时时间(秒)
5. 被动模式(PASV)配置
配置项默认值说明
pasv_enableYES启用被动模式
pasv_min_port-被动模式端口范围下限(如30000
pasv_max_port-被动模式端口范围上限(如31000
pasv_address-服务器公网IP(NAT环境下需指定)
6. 高级选项
配置项默认值说明
listenNO以独立模式运行(YES=IPv4,NO=通过xinetd启动)
listen_ipv6NO启用IPv6监听
tcp_wrappersYES使用TCP Wrappers进行主机访问控制
userlist_enableNO启用用户列表控制(userlist_file指定文件)
userlist_denyYES用户列表中的用户是否被拒绝(YES=黑名单,NO=白名单)

FTP-DEMO Springboot3代码

maven需要引用的包:

    ......<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.5</version><relativePath /> <!-- lookup parent from repository --></parent>    ......<properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.35</version></dependency><dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.11.1</version></dependency><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>......

配置文件:

ftp:# 服务器地址host: 192.168.1.56# 端口号port: 21# 用户名userName: douzi# 密码password: 123456

代码部分:

package com.wd.ftp.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;/*** ftp配置*/
@Configuration
public class FtpConfig {/*** 服务器地址*/private static String host;/*** 端口*/private static Integer port;/*** 用户名*/private static String userName;/*** 密码*/private static String password;@Value("${ftp.host}")public void setHost(String host) {FtpConfig.host = host;}public static String getHost() {return host;}@Value("${ftp.port}")public void setPort(Integer port) {FtpConfig.port = port;}public static Integer getPort() {return port;}@Value("${ftp.userName}")public void setUserName(String userName) {FtpConfig.userName = userName;}public static String getUserName() {return userName;}@Value("${ftp.password}")public void setPassword(String password) {FtpConfig.password = password;}public static String getPassword() {return password;}
}
package com.wd.ftp.util;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;import org.apache.commons.net.ftp.FTPFile;import com.wd.ftp.config.FtpConfig;import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpMode;
import lombok.extern.slf4j.Slf4j;/*** FTP服务工具类*/
@Slf4j
public class FtpUtil {/*** 获取 FTPClient对象*/private static Ftp getFTPClient() {try {if(StrUtil.isBlank(FtpConfig.getHost()) || FtpConfig.getPort() == null|| StrUtil.isBlank(FtpConfig.getUserName()) || StrUtil.isBlank(FtpConfig.getPassword())) {throw new RuntimeException("ftp配置信息不能为空");}Ftp ftp = new Ftp(FtpConfig.getHost(),FtpConfig.getPort(),FtpConfig.getUserName(),FtpConfig.getPassword());//设置为被动模式,防止防火墙拦截ftp.setMode(FtpMode.Passive);return ftp;} catch (Exception e) {e.printStackTrace();log.error("获取ftp客户端异常",e);throw new RuntimeException("获取ftp客户端异常:"+e.getMessage());}}/*** 下载ftp服务器上的文件到本地* @param remoteFile    ftp上的文件路径* @param localFile     输出的目录,使用服务端的文件名*/public static void download(String remoteFile, String localPath) {if(StrUtil.isBlank(remoteFile) || StrUtil.isBlank(localPath)) {return;}Ftp ftp = getFTPClient();try {if(!FileUtil.exist(localPath)){FileUtil.mkdir(localPath);}    File lFile = FileUtil.file(localPath);ftp.download(remoteFile, lFile);} catch (Exception e) {e.printStackTrace();log.error("FTP文件下载异常",e);} finally {//关闭连接try {if(ftp != null)  ftp.close();} catch (IOException e) {throw new RuntimeException(e);}}}/*** 本地文件上传到ftp服务器上* @param remoteDir 上传的ftp目录* @param remoteFileName  保存到ftp服务器上的名称* @param localFile 本地文件全名称*/public static boolean upload(String remoteDir, String remoteFileName, String localFile) {if(StrUtil.isBlank(remoteDir) || StrUtil.isBlank(remoteFileName) || StrUtil.isBlank(localFile)) {return false;}Ftp ftp = getFTPClient();try {File lFile = FileUtil.file(localFile);if(!lFile.exists()) {log.error("本地文件不存在");return false;}if(StrUtil.isBlank(remoteFileName)) {return ftp.upload(remoteDir, lFile);} else {return ftp.upload(remoteDir, remoteFileName, lFile);}} catch (Exception e) {e.printStackTrace();log.error("文件上传FTP异常",e);return false;} finally {//关闭连接try {if(ftp != null)  ftp.close();} catch (IOException e) {throw new RuntimeException(e);}}}/*** 删除FTP服务器中的文件* @param remoteFile    ftp上的文件路径*/public static boolean delFile(String remoteFile) {if(StrUtil.isBlank(remoteFile)) {return false;}Ftp ftp = getFTPClient();try {return ftp.delFile(remoteFile);} catch (Exception e) {e.printStackTrace();log.error("删除FTP服务器中的文件异常",e);return false;} finally {//关闭连接try {if(ftp != null)  ftp.close();} catch (IOException e) {throw new RuntimeException(e);}}}/*** 遍历某个目录下所有文件,不会递归遍历* @param path    目录*/public static List<String> listFile(String path) {List<String> listFile = new ArrayList<>();Ftp ftp = getFTPClient();try {FTPFile[] ftpFiles = ftp.lsFiles(path);for (int i = 0; i < ftpFiles.length; i++) {FTPFile ftpFile = ftpFiles[i];if(ftpFile.isFile()){listFile.add(ftpFile.getName());}}return listFile;} catch (Exception e) {e.printStackTrace();log.error("遍历某个目录下所有文件异常",e);return null;} finally {//关闭连接try {if(ftp != null)  ftp.close();} catch (IOException e) {throw new RuntimeException(e);}}}
}
package com.wd.ftp.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import com.wd.ftp.util.FtpUtil;
import com.wd.ftp.util.SftpUtil;import jakarta.servlet.http.HttpServletRequest;@RestController
public class FtpController {/*** 下载ftp服务器上的文件到本地* @param rf    ftp上的文件路径* @param lp     输出的目录,使用服务端的文件名*/@GetMapping("/download")public String download(@RequestParam(required = false, defaultValue = "1.xml") String rf, @RequestParam(required = false, defaultValue = "/ftp/download/") String lp) {FtpUtil.download(rf, lp);return "success";}}

可以根据FtpUtil扩展SftpUtil的代码,从而支持Sftp模式。

执行效果:

随便造一个1.xml上传ftp

cd /home/douzi 然后查看内容

 

浏览器执行

http://localhost:8080/download

windows下,代码在哪个盘执行,就会生成在哪个盘。

下载成功!其他上传,删除等功能自行试验。

打完收工。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/81094.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis-Flex配置Druid(德鲁伊数据库连接池):Spring Boot 3 集成 MyBatis-Flex 配置 Druid 连接池指南

Spring Boot 3 集成 MyBatis-Flex 配置 Druid 连接池指南 前言 本文详细讲解在 Spring Boot 3 项目中集成 MyBatis-Flex 框架后&#xff0c;如何正确配置 Druid 数据库连接池。针对开发者常见的配置缺失导致启动失败的场景&#xff0c;提供完整的解决方案和原理分析。 前置知识…

安全生产调度管理系统的核心功能模块

安全生产调度管理系统是运用现代信息技术构建的智能化管理平台&#xff0c;旨在实现生产安全风险的全面管控和应急资源的优化调度。该系统通过整合物联网、大数据、人工智能等前沿技术&#xff0c;建立起覆盖风险监测、预警预测、指挥调度、决策支持的全链条安全管理体系。 一…

桃芯ingchips——windows HID键盘例程无法同时连接两个,但是安卓手机可以的问题

目录 环境 现象 原理及解决办法 环境 PC&#xff1a;windows11 安卓&#xff1a;Android14 例程使用的是HID Keyboard&#xff0c;板子使用的是91870CQ的开发板&#xff0c;DB870CC1A 现象 连接安卓手机时并不会出现该现象&#xff0c;两个开发板都可以当做键盘给手机发按…

JavaScript - JavaScript 运算符之圆括号运算符与方括号运算符(圆括号运算符概述、圆括号运算符用法、方括号运算符概述、方括号运算符用法)

一、圆括号运算符概述 圆括号运算符&#xff08;()&#xff09;主要用于函数调用、表达式分组、多种语法结构登 二、圆括号运算符用法 调用函数 function greet() {console.log("Hello!"); }greet();# 输出结果Hello!当箭头函数有多个参数或零个参数时需要括号 c…

AG-UI 协议:重构多模态交互,开启智能应用新纪元

一、协议诞生的时代背景&#xff1a;填补 AI 生态最后一块拼图 在人工智能技术飞速发展的今天&#xff0c;AI 代理&#xff08;Agent&#xff09;作为能够主动执行复杂任务的智能实体&#xff0c;正从实验室走向生产环境&#xff0c;重塑各个行业的工作流程。然而&#xff0c;…

嵌入式学习的第二十天-数据结构-调试+链表的一般操作

一、调试 1.一般调试 2.找段错误 二、链表的一般操作 1.单链表的修改 int ModifyLinkList(LinkList*ll,char*name,DATATYPE*data) {DATATYPE * tmp FindLinkList(ll, name);if(NULL tmp){return 1;}memcpy(tmp,data,sizeof(DATATYPE));return 0; } 2.单链表的销毁 int D…

如何同时管理不同平台的多个账号?

在当今数字营销、电商运营、跨境贸易盛行的时代&#xff0c;同时管理多个平台的账号几乎成了从业者的标配。无论是做社媒营销的广告主&#xff0c;还是操作亚马逊、eBay、Shopee 等平台的跨境卖家&#xff0c;多账号运营都是提升曝光、分散风险、扩大收益的重要方式。 然而&am…

STM32外设AD/DA-基础及CubeMX配置

STM32外设AD/DA-基础及CubeMX配置 一&#xff0c;什么是AD/DA二&#xff0c;基础概念1&#xff0c;模拟 vs 数字2&#xff0c;AD转换1&#xff0c;分辨率 (Resolution)2&#xff0c;参考电压 (Reference Voltage, Vref)3&#xff0c;采样率 (Sampling Rate) 3&#xff0c;DA转换…

【软考 霍夫曼编码的文档压缩比】

霍夫曼编码的文档压缩比计算基于字符频率的最优编码分配&#xff0c;以下是详细步骤及相关案例&#xff1a; 一、压缩比计算公式 [ \text{压缩比} \frac{\text{压缩前总比特数}}{\text{压缩后总比特数 编码表存储开销}} ] 通常以 比率&#xff08;如 3:1&#xff09; 或 百分…

关闭VSCode 自动更新

参考&#xff1a;关闭VSCode 自动更新_vscode关闭自动更新-CSDN博客 vscode的设置 Update: Mode Update: Enable Windows Background Updates Extensions: Auto Check Updates Extensions: Auto Update

Flask框架搭建

1、安装Flask 打开终端运行以下命令&#xff1a; pip install Flask 2、创建项目目录 在Windows上&#xff1a; venv\Scripts\activate 执行 3、创建 app.py 文件 可以在windows终端上创建app.py文件 &#xff08;1&#xff09;终端中创建 使用echo命令 echo "fr…

5G-A和未来6G技术下的操作系统与移动设备变革:云端化与轻量化的发展趋势

目录 5G技术带来的革命性变革 云端化操作系统的实现路径 完全云端化模式 过渡性解决方案 未来操作系统的发展方向 功能架构演进 安全机制强化 移动设备的形态变革 终端设备轻量化 物联网设备简化 实施挑战与应对策略 技术挑战 商业模式创新 总结与展望 5G技术作为…

【漫话机器学习系列】261.工具变量(Instrumental Variables)

工具变量&#xff08;Instrumental Variables&#xff09;通俗图解&#xff1a;破解内生性困境的利器 在数据建模与因果推断过程中&#xff0c;我们经常遇到一个棘手问题&#xff1a;内生性&#xff08;Endogeneity&#xff09;。它会导致模型估计产生偏差&#xff0c;进而误导…

CSS:颜色的三种表示方式

文章目录 一、rgb和rgba方式二、HEX和HEXA方式&#xff08;推荐&#xff09;三、hsl和hsla方式四、颜色名方式 一、rgb和rgba方式 10进制表示方法 二、HEX和HEXA方式&#xff08;推荐&#xff09; 就是16进制表示法 三、hsl和hsla方式 语法&#xff1a;hsl(hue, satura…

支付宝授权登录

支付宝授权登录 一、场景 支付宝小程序登录&#xff0c;获取用户userId 二、注册支付宝开发者账号 1、支付宝开放平台 2、点击右上角–控制台&#xff0c;创建小程序 3、按照步骤完善信息&#xff0c;生成密钥时会用到的工具 4、生成的密钥&#xff0c;要保管好&#xff…

涂色不踩雷:如何优雅解决 LeetCode 栅栏涂色问题

文章目录 摘要描述例子&#xff1a; 题解答案&#xff08;Swift&#xff09;题解代码分析动态规划核心思路初始条件 示例测试及结果示例 1&#xff1a;示例 2&#xff1a;示例 3&#xff1a; 时间复杂度空间复杂度总结实际场景联系 摘要 在用户体验和界面设计中&#xff0c;颜…

GEE计算 RSEI(遥感生态指数)

&#x1f6f0;️ 什么是 RSEI&#xff1f;为什么要用它评估生态环境&#xff1f; RSEI&#xff08;遥感生态指数&#xff0c;Remote Sensing Ecological Index&#xff09; 是一种通过遥感数据计算得到的、综合反映区域生态环境质量的指标体系。 它的设计初衷是用最少的变量&…

图像处理:预览并绘制图像细节

前言 因为最近在搞毕业论文的事情&#xff0c;要做出一下图像细节对比图&#xff0c;所以我这里写了两个脚本&#xff0c;一个用于框选并同时预览图像放大细节&#xff0c;可显示并返回框选图像的坐标&#xff0c;另外一个是输入框选图像的坐标并将放大的细节放置在图像中&…

基于javaweb的SSM驾校管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

限制 MySQL 服务只能被内网 `192.168.1.*` 网段的设备访问

1. 修改 MySQL 配置文件 MySQL 默认监听所有网络接口(0.0.0.0),需要将其绑定到内网 IP 地址或限制访问范围。 (1)编辑 MySQL 配置文件 找到 MySQL 的主配置文件,通常是 /etc/my.cnf 或 /etc/mysql/my.cnf。使用文本编辑器打开: sudo vi /etc/my.cnf(2)设置 bind-a…