
简介面向 Java 开发者的 FTP/SFTP 文件传输工具资源重点解决从服务器上传、下载文件时连接配置和代码实现不清晰的问题。内容按实战链路梳理了 FTP 客户端连接、登录验证、主动/被动工作模式切换、二进制与 ASCII 文件类型设置、目录切换、异常处理等核心知识点并给出可在项目中参考的 Java 示例代码便于理解完整调用流程。压缩包体量非常轻量仅含 2 个文件1 个 Java 源码文件和 1 个 JSch 0.1.24 版 jar 包整体约 106KB其中 JSch 库用于支持基于 SSH 的 SFTP 安全传输实用性强。这类实现特别适合初学 Java 网络编程、需要在服务器间自动化搬运文件或对比 FTP 与 SFTP 用法的开发者也可作为小型工具模块直接嵌入业务代码。目前已有 251 人学习下载源码中封装了上传、下载、目录操作等常见方法配合注释或示例可快速迁移使用节省自行搭建与排错时间。1. FTP 从服务器上传下载文件为什么现在还得学它如果你在 2025 年还搜“ftp 从服务器上传下载文件”大概率不是想了解理论而是有个具体任务卡住了某台老服务器只开了 21 端口某套业务系统还在用 FTP 做数据交换或者某个嵌入式设备固件只能通过 FTP 拉取。FTP 确实老了明文传输、被动模式折腾人、断点续传要看双方脸色但它在内网运维和生产环境里依然大量存在短期内不会被替换掉。这篇文章不聊概念直接聊怎么用命令行和脚本把文件从服务器拉下来、推上去以及最让人头疼的被动模式、超时、乱码和断点续传问题。新手照着敲命令就能跑熟手能直接拿走参数模板和避坑清单。我见过太多人死在同样的几个地方内网能 ping 通但 FTP 连不上、上传大文件到一半断了、下载下来的文件名乱码、脚本定时跑三天就卡死。这些问题不是玄学基本都是主动模式和被动模式没搞明白、超时参数没调、连接没复用导致的。把这几件事理清楚FTP 其实是个很听话的工具。2. FTP 的本质与选型搞懂两种模式你就避开了 80% 的坑2.1 主动模式PORT与被动模式PASV到底差在哪FTP 和其他协议最大的区别是它用两个连接一个控制连接端口 21管命令一个数据连接实际传文件。很多人第一次配置 FTP 失败就是没搞清数据连接是怎么建立的。默认情况下标准 FTP 是主动模式客户端在控制连接上发 PORT 命令告诉服务器“你连我机器上的某某端口吧”然后服务器主动去连客户端的那个端口。这在客户端有公网 IP 的年代没问题但放到现在客户端几乎都在 NAT 后面服务器根本连不进来于是就有了被动模式。被动模式PASV是客户端发 PASV 命令服务器开启一个随机端口并告诉客户端“你来连我这个端口”数据连接由客户端发起。这解决了 NAT 问题但也带来了新麻烦服务器的防火墙必须放行一段随机端口范围否则数据连接照样建不起来。判断当前用的是哪种模式用命令行工具看输出# 使用 lftp 连接开启调试输出会打印 PORT/PASV 指令 lftp -d -u username,password ftp://192.168.1.100 -e ls; quit逻辑说明-d参数打开调试模式控制连接上发送和接收的每条 FTP 指令都会打印出来。如果看到PASV字样说明在尝试被动模式看到PORT说明在尝试主动模式。-e ls; quit是连接成功后执行目录列表然后退出相当于一次探测。参数说明-u username,password指定登录凭据也可以不写等交互式输入。实际排障时我会先把这条命令跑一遍确认服务器支持哪种模式再决定客户端怎么配置。2.2 选型curl、lftp、sftp 还是 Python 脚本FTP 客户端工具非常多但生产环境里值得长期依赖的就几个。选型不看哪个新看哪个能稳定扛住脚本化任务。curl 是最通用的选择几乎每台 Linux 机器都有支持 FTP 的上传下载、断点续传但不擅长复杂的目录递归操作。lftp 是为 FTP 而生的交互式工具支持镜像、并行传输、断点续传、自动重试是脚本化任务的首选。sftp 走的是 SSH 通道如果服务器支持 SSH 就别用 FTP 了加密、断点续传、权限控制全都有唯一的缺点是它不是 FTP没法对接老系统。Python 的 ftplib 是标准库适合写复杂逻辑比如先判断文件大小再决定是否下载、解析目录列表提取文件属性。我一般这样安排一次性的文件操作用 curl定时批量同步用 lftp需要嵌入业务系统用 Python。原则很简单——能少装依赖就少装能用系统自带就不引入新组件。# 安装 lftp常见发行版直接包管理器安装 apt-get install lftp # Debian/Ubuntu 系 yum install lftp # CentOS/RHEL 系逻辑说明lftp 几乎覆盖了 FTP 日常所有需求没有特殊理由不建议用别的。安装包不大但带来的重试机制和镜像同步能力非常省事。参数说明不同发行版包名都是 lftp没有变化。如果公司内网有统一软件源直接走源安装即可。3. 从服务器下载文件到本地命令模板与实战参数3.1 最小可用命令从登录到下载先看一个最简单的下载场景从服务器把 /data/backup/ 目录下的 app_20250101.tar.gz 拉到本地当前目录。# 用 lftp 一次性下载单个文件 lftp -u ftpuser,密码 -e get /data/backup/app_20250101.tar.gz -o ./quit ftp://192.168.1.100逻辑说明命令拆开看-u ftpuser,密码是用户名密码-e后面跟着一段 lftp 内部指令get是下载命令-o ./指定输出到当前目录quit关闭连接。注意密码在单引号里避免特殊字符被 shell 吃掉。参数说明服务器地址写在最后一定要带ftp://前缀否则 lftp 不识别协议。get命令的-o参数可以指定本地保存路径不写的话默认保存在当前目录且使用远端文件名。匿名登录就把-u去掉。实际生产环境不建议把密码直接写在命令行里用~/.netrc文件更安全# 编辑 ~/.netrc 文件保存登录信息 cat ~/.netrc EOF machine 192.168.1.100 login ftpuser password 密码 EOF chmod 600 ~/.netrc逻辑说明.netrc是 FTP/curl 等工具通用的凭据文件machine指定主机地址login和password分别是用户名密码。注意权限必须设置为 600否则工具会拒绝读取。参数说明machine后的地址要和连接地址完全一致包括协议写法lftp 内部会做匹配。要清理凭据直接删掉文件里的对应三行即可。3.2 目录递归下载镜像整个远程目录单个文件下载太简单实际工作中更多是同步整个目录。lftp 的 mirror 命令是干这个的# 镜像下载远程 /data/public/ 到本地 ./public_backup/ lftp -u ftpuser,密码 ftp://192.168.1.100 -e mirror --delete --parallel4 /data/public ./public_backupquit逻辑说明mirror是 lftp 的目录镜像命令/data/public是远端目录./public_backup是本地目录。--delete让本地存在但远端不存在的文件也被删除保证两边完全一致如果只想增量下载不想删文件就不要加这个参数。--parallel4表示同时下载 4 个文件充分利用带宽。参数说明--parallel后面的数字不是越大越好受服务器带宽和 IO 限制一般 4 到 8 比较合理服务器性能差就调到 2。mirror默认会把子目录一起下载不需要额外参数。还有一个有用的参数是--continue断点续传下载中断后重新执行会自动判断进度继续传。3.3 用 curl 实现单文件和批量下载有些机器上没有 lftp或者你不想为了一次下载装新工具curl 就是备选方案。单文件下载很简单# 用 curl 下载单文件支持断点续传 curl -u ftpuser:密码 -O ftp://192.168.1.100/data/public/app.tar.gz --continue-at -逻辑说明-O保持远端文件名保存到本地--continue-at -让 curl 自动断点续传本地已有部分文件时接着传而不是重新开始。不带这个参数的话下载中断后重新执行会从头下载。参数说明-u后面跟用户名冒号密码。--continue-at -的写法是固定语法前半部分写明从哪个字节开始续传单独写-表示自动检测本地文件大小。批量下载时先拿到文件清单再用循环逐条拉取# 循环下载整个目录下的所有 .tar.gz 文件 for file in $(curl -u ftpuser:密码 ftp://192.168.1.100/data/public/ | awk {print $NF})do if [[ $file *.tar.gz ]]; then curl -u ftpuser:密码 -O ftp://192.168.1.100/data/public/$file --continue-at - fi done逻辑说明curl直接访问 FTP 目录 URL 会输出目录列表用awk取最后一列拿到文件名再判断后缀。实际生产中更稳的方式是用curl --list-only直接只列文件名这样就节省了 awk 解析的步骤。这里写--list-only是更推荐的做法。参数说明$file变量注意加引号防止文件名里有空格导致命令被拆开。--continue-at -同样保留断点续传能力。这个循环每次都重新建立 FTP 连接文件多时效率一般量大的场景还是建议用 lftp。4. 从本地向服务器上传文件反向操作更考验细节4.1 单文件上传与覆盖策略上传比下载容易出问题的原因是容易覆盖不该覆盖的文件。先看最基础的# 用 lftp 上传单个文件到服务器指定目录 lftp -u ftpuser,密码 -e put /local/path/app.tar.gz -o /data/upload/app.tar.gzquit ftp://192.168.1.100逻辑说明put是 lftp 的上传命令-o指定服务器上保存的路径和文件名。如果远端已经有同名文件默认直接覆盖没有提示。参数说明-o可以不写这时文件会传到登录后的默认目录文件名保持不变。要注意 if 远端目录不存在上传会直接报错lftp 不会自动创建目录需要先执行mkdir -p。curl 上传单文件是另一个思路# 用 curl 把本地文件推到服务器 curl -u ftpuser:密码 -T ./app.tar.gz ftp://192.168.1.100/data/upload/app.tar.gz逻辑说明-T指定要上传的本地文件URL 里的路径是远端保存地址。与 lftp 类似远端目录不存在也会报错。参数说明-T后面的路径写本地路径远端路径写在 URL 里两者可以不同名。如果想上传后保留原文件名URL 直接写目录路径即可。4.2 目录递归上传与增量同步上传整个目录比下载目录更容易踩坑因为本地目录结构差异会导致远端多出垃圾文件。用 mirror 反向同步# 把本地 ./build_output/ 上传到远端 /data/www/ lftp -u ftpuser,密码 -e mirror -R --only-newer ./build_output /data/wwwquit ftp://192.168.1.100逻辑说明mirror -R是反向镜像本地往远端同步。--only-newer只上传比远端新的文件适用于增量发布。不带--only-newer的话会全部覆盖上传。参数说明增量同步的关键是服务器时间不能差的太多如果服务器时间和本地差了十几分钟--only-newer判断会不准要么同步失败要么多传文件。-R后面本地目录在前远端目录在后顺序反了会把远端内容拉到本地来。4.3 上传脚本的完整写法生产环境的定时上传不能只靠手敲命令一个带日志、带失败重试的脚本才能稳定跑。下面是一个完整的定时上传脚本模板#!/bin/bash # 定时上传脚本带日志和重试机制 FTP_HOST192.168.1.100 FTP_USERftpuser FTP_PASS密码 LOCAL_DIR./build_output REMOTE_DIR/data/www LOG_FILE./upload.log log() { echo [$(date %Y-%m-%d %H:%M:%S)] $1 $LOG_FILE } upload() { for i in 1 2 3; do lftp -u $FTP_USER,$FTP_PASS -e mirror -R --only-newer $LOCAL_DIR $REMOTE_DIRquit ftp://$FTP_HOST { log 上传成功 return 0 } log 第 $i 次上传失败5秒后重试 sleep 5 done log 3次重试均失败停止上传 return 1 } upload逻辑说明log函数统一写日志方便事后排查。upload函数里套了三层重试每次失败等待 5 秒重新执行 lftp 命令。保证了 lftp 退出码为 0成功才记录成功日志否则继续重试。参数说明这个脚本只处理了上传逻辑实际用的时候建议把本地目录LOCAL_DIR改成绝对路径避免计划任务执行时相对路径解析不一致。重试次数和等待时间可以根据服务器稳定性调整网络差的地方可以试 5 次等待 10 秒。5. FTP 踩坑实录被动模式、超时、乱码与文件损坏5.1 可以连上但无法列出目录被动模式端口段没放行现象lftp能登录执行ls的时候卡住很久最后报Connection timed out或Unable to establish data connection。原因服务器在被动模式下向客户端开放了一个随机端口客户端的防火墙阻止了数据连接。或者服务器端的防火墙根本没有放行被动端口段导致客户端连接无响应。解决先看服务器上 vsftpd 的配置确认被动端口范围然后在服务端防火墙放行对应端口段。客户端这边检查本机防火墙是否限制了出站端口。# 在服务器上查看 vsftpd 配置中的被动端口设置 grep -i pasv /etc/vsftpd.conf逻辑说明pasv_min_port和pasv_max_port这两个配置项定义了被动模式下服务器开放的端口范围比如 30000 到 31000。如果没设置vsftpd 默认任意高端口防火墙没法精确放行。建议在配置里固定一个范围然后防火墙只放这一段的 TCP 入站。参数说明修改 vsftpd.conf 后要重启 vsftpd 服务才生效命令是systemctl restart vsftpd。客户端排查时可以在本机用nc -vz 服务器IP 端口号测试数据端口通不通通不了就是被拦了。5.2 大文件传一半断开超时时间太短与缺 IP 层保活现象上传或下载超过 500MB 的文件传了 40% 或 60% 就报连接中断重新执行又从 0 开始。原因FTP 默认的传输超时是按秒计的控制连接长时间没有数据包来回中间设备的 NAT 会话就过期了。或者 lftp 的net:timeout设置太短长时间没数据传输就被判定超时。解决调整 lftp 超时参数并让控制连接持续保活。# 设置更长的超时时间并保活控制连接 lftp -u ftpuser,密码 -e set net:timeout 60set net:reconnect-interval 5get /data/bigfile.iso -o ./bigfile.isoquit ftp://192.168.1.100逻辑说明net:timeout 60把超时从默认值拉长到 60 秒net:reconnect-interval 5在连接意外断开后等 5 秒自动重连。这样长文件传输中即使网络有短暂波动也不会立刻判死。参数说明net:timeout取值范围根据网络质量来内网可以设 15 到 30 秒跨机房或公网建议 60 秒。传输超过 2GB 的文件建议同时加上net:max-retries 5给重连加次数上限。5.3 文件名中文乱码远端字符集与本地不一致现象下载的文件名在本地显示为??????.pdf或乱码文件内容没问题但文件名完全不可读。原因FTP 协议本身不协商字符集服务器用 UTF-8 存储文件名客户端却用 GBK 解析或者反过来。大多数中文乱码都是这个原因。解决在 lftp 里指定远程字符集与本地字符集的转换关系。# 让 lftp 在显示/下载时完成字符集转换 lftp -u ftpuser,密码 -e set ftp:charset UTF-8set file:charset GBKlsquit ftp://192.168.1.100逻辑说明ftp:charset指定服务器上的文件名编码file:charset指本地文件系统使用的编码。lftp 在两者不一致时会自动转换。如果服务器是 GBK 而本地是 UTF-8把两个值调换即可。参数说明这条设置在下载时同样生效不只是命令行的ls显示。遇到乱码先别急着改代码确认服务器编码是最快路径用filezilla连一下看能正常显示就照着它的字符集设置来。5.4 下载下来的文件损坏用了 ASCII 模式传二进制现象从服务器下载压缩包本地解压报错md5sum比对和服务器上不一致但文件大小差不多。原因FTP 有两种传输模式ASCII 模式会在传输过程中做换行符转换把\n转成\r\n或反过来。二进制文件被当成文本传数据就被改坏了。解决在 lftp 中强制使用二进制模式并在传输前先对比文件大小。# 上传下载都强制二进制模式 lftp -u ftpuser,密码 -e set ftp:binary yesget /data/app.tar.gz -o ./app.tar.gzquit ftp://192.168.1.100逻辑说明ftp:binary yes等价于在交互模式里输入binary命令告诉服务器按字节流传输不做任何转换。默认情况下 lftp 对.tar.gz、.zip、.iso这类文件不该用 ASCII但保险起见还是显式声明。参数说明下载完成后立即用md5sum本地文件和服务器上文件的校验和做比对这是确认文件没有损坏的最快方式。如果服务器上没有md5sum命令可以在 FTP 服务器端生成 MD5 文件供下载比对。5.5 lftp 脚本卡死没有设置空闲超时现象定时脚本跑了一段时间后不再工作进程挂在那里日志里没有报错第二天一看没有执行任何上传下载。原因FTP 连接空闲一段时间后被服务器或网络设备断开但 lftp 客户端没有检测到依然傻等。如果没有设置超时它就永远挂起。解决脚本里设置空闲超时和连接超时让 lftp 在断连后自动退出进而在外层脚本重试。# 设置网络空闲超时避免脚本挂起 lftp -u ftpuser,密码 -e set net:timeout 30set net:max-retries 2mirror -R --only-newer ./local /remotequit ftp://192.168.1.100逻辑说明net:timeout 30是总超时时间net:max-retries 2是连接失败重试最多两次。配合外层脚本的重试逻辑即使连接断掉也会在规定时间内报错退出而不是无限期等待。参数说明定时任务里建议额外加一个执行时间上限用timeout 300 lftp ...包裹超过 5 分钟直接杀掉进程防止意外情况卡死整个任务链。核心原则是脚本不能裸奔必须有超时保护。6. 用 lftp 脚本实现无人值守的全量备份与增量同步到了生产环境单条命令解决不了持续备份的问题。我维护的模拟项目X里每天凌晨两点要把一台业务服务器上的指定目录完整备份到备份机保留最近 7 天的版本然后只同步新增文件。这个需求用 lftp 加 shell 脚本就能扛住。#!/bin/bash # 全量备份 增量同步脚本 BACKUP_DIR./backup_$(date %Y%m%d) REMOTE_DIR/data/business mkdir -p $BACKUP_DIR # 全量镜像远端目录到当天备份目录 lftp -u ftpuser,密码 -e mirror --delete $REMOTE_DIR $BACKUP_DIRquit ftp://192.168.1.100 # 保留最近7天备份清理更早的目录 find ./backup_* -maxdepth 0 -type d -mtime 7 -exec rm -rf {} \; # 增量上传新文件到远端 archive 目录 lftp -u ftpuser,密码 -e set net:timeout 30mirror -R --only-newer ./new_files /data/archivequit ftp://192.168.1.100逻辑说明第一部分把服务器业务目录完整拉到以日期命名的本地目录里--delete保证本地不留远端已删除的残留文件。第二部分用find清理 7 天前的目录避免备份占满磁盘。第三部分把本地新产生的文件增量推到服务器的归档目录--only-newer只上传新的和改动过的。参数说明find的-mtime 7语义是修改时间超过 7 天的目录如果你上传的文件会自动更新本地时间戳以当天为准不会有问题。增量同步的判断依赖文件修改时间如果上传的文件 mtime 没有变化--only-newer就不会传它这是正常的。如果确实需要每次都传去掉--only-newer即可量大会慢很多。验证这套方案的稳定性我习惯在脚本末尾加一段统计输出记录每次同步的文件数量和总大小。跑两周后看日志基本能判断服务器和网络是否有异常波动。日志里一旦出现反复重试的情况就用前面 2.1 小节的调试命令抓一下 FTP 交互过程看是主动被动模式问题还是端口问题。这套方案最值钱的地方在于--delete和--only-newer的组合——一个管全量一致性一个管增量效率。很多人用 FTP 同步目录只做单向上传远端删掉的文件本地永远不删时间长了两边就完全对不上。加了--delete之后每次同步都保证远端和本地严格一致这才是真正可用的备份方案。如果你一开始不放心可以在测试目录里跑跑一周确认无误再接真实业务目录。希望帮到你。本文还有配套的精品资源点击获取