简介面向Java开发者的FTP上传下载工具资源基于Apache Commons Net库实现完整覆盖连接服务器、登录验证、主动/被动模式切换、上传下载文件、切换目录、设置二进制传输类型以及安全断开连接等核心操作并附带所需依赖jar包适合需要在业务系统中快速集成FTP能力的开发者参考或直接复用。压缩包共2个文件包括一个Java工具类源码和一个jar包整体仅106KB结构精简、无冗余代码。工具类对连接与登录过程做了封装使用者只需指定服务器、端口、账号密码即可调用上传和下载方法有效减少重复代码。已有250人学习浏览源码中给出了明确的调用流程与异常处理示例可帮助理解FTPClient常用API的用法同时吸收被动模式设置、资源关闭等实践细节在自有项目中灵活实现文件传输功能。1. 为什么还在用 FTP 做服务器上传下载文件先说结论在内外网隔离、设备对接和批量文件同步这类场景里ftp从服务器上传下载文件依然是性价比最高的选择。HTTP 上传要写接口、要处理 sessionSMB 在跨平台和跨网段时权限配置能让人折腾一整天而 FTP 只要有 21 端口、一个用户名密码几乎任何语言和系统都能在两分钟内跑通。我做运维这些年见过打印机扫描到共享文件夹三天两头掉权限换成 FTP 之后一两年没再动过配置。这篇笔记不打算讲那些花里胡哨的替代方案而是把「从服务器上传下载文件」这件事拆成三层协议原理、服务端搭建、客户端落地。你会看到最小可用的配置命令、被动模式和主动模式的区别、权限和防火墙的坑以及用脚本做定时同步的具体写法。适合刚接触 FTP 的运维新手也适合被权限和断点续传问题折磨过的熟手照着做就能复现。2. FTP 上传下载的核心机制先弄清数据连接和模式再谈命令2.1 两个通道控制连接与数据连接FTP 和 HTTP 最大的不同是它有两个连接控制连接走 21 端口用来传指令比如 USER、PASS、CWD、RETR、STOR数据连接则另开一个端口用来传真正的文件内容。很多新手第一次搭 FTP 服务器发现能登录但传不了文件问题几乎都出在数据连接上。数据连接的建立方式分两种主动模式PORT和被动模式PASV。主动模式下客户端先告诉服务器「我开了一个端口等你连」然后服务器主动去连客户端的那个端口被动模式下服务器告诉客户端「我开了一个端口你来连我」客户端再去连。主动模式在客户端有公网 IP 时没问题但客户端在 NAT 后面就废了被动模式反过来服务器要放行一段被动端口范围。所以你会看到内网里大家普遍用 PASV外网访问时则要把被动端口段在防火墙和安全组里一并放行只开 21 端口的防火墙是典型的配置错误。我在日常排障时第一步永远是确认服务器用的是哪种模式再去看对应端口是否放通。2.2 传输模式ASCII 与 Binary 的隐藏坑FTP 默认有两种传输类型ASCII 和 Binary。ASCII 模式会把换行符做转换这在纯文本文件里没问题但一旦传压缩包、镜像、可执行文件就会出现文件大小变了、解压失败等诡异现象——因为二进制数据被按行处理了。所以正确做法是明确设置传输模式为 Binary尤其是压缩包和程序文件。命令行下用binary指令脚本里用TYPE I图形客户端里一般叫「二进制模式」。这个细节在单片机和打印机等设备对接的场景尤其重要因为设备端 Fw 往往默认 ASCII 模式你在电脑端用 Binary 传两端不一致就会出现大小对不上、文件头尾残缺的现象。2.3 基础交互命令的最小集不管用什么语言、什么工具FTP 命令层的核心就那几条。我整理了一张对照表方便你在纯命令行环境下救急命令作用常用参数open连接服务器open 192.168.1.100 21user登录用户名user adminpass登录密码pass 123456binary切换二进制模式无关键passive切换被动模式无关键ls / dir查看目录ls /data/backupcd / lcd切换远端/本地目录cd /datalcd D:\tmpget下载单个文件get file.zipmget下载多个文件mget *.zipput上传单个文件put file.zipmput上传多个文件mput *.logdelete删除远端文件delete old.logbye / quit退出无提示如果在get之后发现文件大小和源文件不一致先查是否用了 binary这能排除八成故障。这组命令配合脚本就能实现定时任务后面第四章我会演示完整的上传下载脚本写法。3. 服务器端搭建与三个必调参数3.1 用 vsftpd 搭建一个可用的 FTP 服务器常见做法是用 vsftpd它轻量、稳定、配置简单CentOS 系和 Ubuntu 系都能用一行命令装上。以下以 Ubuntu 为例# 安装 sudo apt update sudo apt install -y vsftpd # 备份原始配置 sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.bak # 编辑配置 sudo vim /etc/vsftpd.conf修改配置文件里这几个关键项anonymous_enableNO local_enableYES write_enableYES local_umask022 dirmessage_enableYES xferlog_enableYES connect_from_port_20YES pasv_enableYES pasv_min_port30000 pasv_max_port31000 pam_service_namevsftpd这段配置的含义是禁止匿名登录允许本地用户登录开放写权限这决定了能不能上传文件日志记录到 xferlog启用被动模式并指定端口范围。local_umask022决定新建文件权限为 644、目录为 755如果你希望别人上传的文件默认可被其他用户读取这个值很重要。保存后重启服务sudo systemctl restart vsftpd sudo systemctl enable vsftpd3.2 三个必调参数write_enable、pasv_min_port、local_umask排障最多的三个参数值得单独拿出来说第一个是write_enableYES。默认配置里这个值常常是注释状态插件能登录、能看目录但一上传就报550 Permission denied。很多老手的做法是直接chmod 777目录这其实没用——问题不在目录权限而是 FTP 服务本身没放写权限。第二个是pasv_min_port和pasv_max_port。如果不指定vsftpd 会动态分配随机端口防火墙就很难放行。全开防火墙固然省事但不安全所以指定一个窄区间30000-31000 足够日常并发使用。注意这个范围和防火墙规则、云安全组要一一对应。第三个是local_umask022。它决定上传文件的默认权限。如果不改默认可能是 077文件上传后只有上传者本人能读。涉及多账号协作时权限不够会引发文件传上去了但应用读不了的连锁故障。3.3 Windows 和嵌入式设备上的常见替代配置Windows 上常见做法是用 IIS 的 FTP 服务或 FileZilla Server。以 FileZilla Server 为例安装后在监听端口、被动模式范围这两处和 Linux 上的理念完全一致指定被动端口范围然后在防火墙里放行。IIS 的 FTP 则要在「FTP 防火墙支持」里填写服务器的外部 IP 地址和端口范围否则外网用户会拿到内网 PASV 地址导致一连就超时。打印机、触摸屏这类嵌入式设备一般不带完整的 FTP 服务端它们更常用的是 FTP 客户端扫描上传功能比如美能达、柯美的扫描到 FTP。配置重点在两点一是服务器地址填 IP 而不是机器名家用路由器的 DNS 解析不可靠二是目录路径不要带空格和中文字符很多设备的 FTP 实现对 UTF-8 和转义支持得很差路径一复杂就报错。这个我在后面第四章会再展开讲一次因为服务器和打印机联调时的故障边界和纯服务器场景不太一样。4. 客户端实操命令行一键上传下载脚本与断点续传4.1 用 bash 脚本实现上传下载的最小完整案例图型客户端能解决单次操作但不能解决重复劳动。定时备份、批量同步这种场景用脚本才是正解。下面是我常用的一段脚本覆盖了最主要的上传下载流程#!/bin/bash # FTP 上传下载脚本支持单文件、多文件 # 依赖系统自带 ftp 命令无需额外安装 FTP_IP192.168.1.100 FTP_USERbackup FTP_PASSBackup2024 REMOTE_DIR/data/backup LOCAL_DIR/home/user/backup # 上传本地文件到服务器 upload_file() { local filename$1 ftp -n $FTP_IP EOF user $FTP_USER $FTP_PASS binary cd $REMOTE_DIR put $filename bye EOF echo upload $filename done } # 从服务器下载文件 download_file() { local filename$1 ftp -n $FTP_IP EOF user $FTP_USER $FTP_PASS binary cd $REMOTE_DIR lcd $LOCAL_DIR get $filename bye EOF echo download $filename done } # 下载服务器上某个目录下的全部 zip download_all_zip() { ftp -n $FTP_IP EOF user $FTP_USER $FTP_PASS binary cd $REMOTE_DIR lcd $LOCAL_DIR mget *.zip bye EOF } # 调用示例先下载再上传保持双向同步 download_all_zip upload_file today_report.log这段脚本的核心逻辑是通过ftp -n打开一个非交互式会话然后用 heredoc 把命令依次喂进去。binary确保文件不被转行cd和lcd分别切换远端与本地目录。如果本地目录不存在lcd会报错所以脚本开头要先mkdir -p $LOCAL_DIR。需要注意脚本里的密码是明文写在文件里的这在生产环境是个安全隐患。常见做法是把权限收紧到只有 root 可读同时用chmod 600保护脚本本身。如果是多台机器共用的脚本也可以把账号密码放到单独的配置文件里和脚本解耦。4.2 断点续传文件传一半断了怎么办FTP 自带的restart命令支持断点续传但纯命令行脚本很少用它因为要手动记录偏移量太繁琐。实际工程里更常见的是用curl或lftp这两个工具都内置断点续传参数。下面用 lftp 演示# lftp 断点续传下载 lftp -u $FTP_USER,$FTP_PASS $FTP_IP -e set ftp:ssl-allow no set net:timeout 30 set net:max-retries 3 mirror --continue --parallel4 /data/backup /home/user/backup bye 这里的mirror --continue就是断点续传的关键它会对比远端和本地文件大小文件不完整就接着下完。--parallel4表示同时开 4 个连接下载不同文件。注意set net:timeout 30和set net:max-retries 3是网络容错的核心配置没有的话大文件传输遇到一次瞬断就失败了。上传也有对应的断点续传写法在 lftp 的mirror --reverse --continue即是相反方向。实际使用中我一般只在文件很大、比如几个 GB 的数据库备份时才会用断点续传几十 MB 的文件直接重传的代价比维护断点逻辑低得多。4.3 用 Python 在 Windows 上做定时上传下载Windows 上没有现成的 ftp 命令配合计划任务也还行但生产环境更稳的做法是写个 Python 脚本用ftplib标准库。这个库功能简单直接且不依赖第三方包在 Windows 的 Python 环境里直接就能跑# -*- coding: utf-8 -*- # Python FTP 定时上传下载示例适用 Windows/ Linux from ftplib import FTP import os, sys SERVER 192.168.1.100 USER backup PASSWD Backup2024 def ftp_connect(): ftp FTP() ftp.connect(SERVER, 21, timeout30) ftp.login(USER, PASSWD) # 强制二进制模式防止文件损坏 ftp.sendcmd(TYPE I) return ftp def download_file(ftp, remote_path, local_path): os.makedirs(os.path.dirname(local_path), exist_okTrue) with open(local_path, wb) as f: ftp.retrbinary(fRETR {remote_path}, f.write, blocksize8192) print(fdownloaded: {remote_path} - {local_path}, flushTrue) def upload_file(ftp, local_path, remote_path): with open(local_path, rb) as f: ftp.storbinary(fSTOR {remote_path}, f, blocksize8192) print(fuploaded: {local_path} - {remote_path}, flushTrue) if __name__ __main__: ftp ftp_connect() # 下载单个文件 download_file(ftp, /data/backup/x.zip, ./x.zip) # 上传单个文件 upload_file(ftp, ./today.log, /data/backup/today.log) ftp.quit()这里的核心逻辑是retrbinary和storbinary底层就是发 RETR 和 STOR 指令但帮你处理了数据块的读取和写入。blocksize8192是缓冲区大小千兆网内网环境下 8KB 到 64KB 都不错太小会拖慢速度太大会占内存。sendcmd(TYPE I)等价于命令行里的binary指令千万不能省略。本章的最后说一件被忽略的小事服务端日志。vsftpd 的日志路径默认在/var/log/xferlog或/var/log/vsftpd.log记录了每次传输的文件名、大小、耗时、用户 IP。碰到「文件传完了但对方说没收到」这种扯皮场景第一反应该是去看 xferlog它比两边互相猜更接近真相。我随手截一条典型日志字段含义日志字段含义Mon DD HH:MM:SS传输开始的时间戳192.168.1.50客户端来源 IP/data/backup/x.zip传输的文件路径b传输模式是 binaryo方向是输出下载i 是输入上传1024000传输字节数5. 避坑指南权限、防火墙和打印机这类特殊客户端的 5 个常见问题5.1 登录成功但目录列表为空或长时间转圈现象命令行能登录pass 也通过了但执行ls时卡住不动几十秒后报超时。原因服务器开了 PASV 模式但没有放行被动端口范围或者防火墙只放行了 21 端口。客户端在 PASV 模式下去连服务器的数据端口连接被防火墙规则拦掉了。解决检查 vsftpd.conf 里pasv_min_port和pasv_max_port是否启用然后在防火墙放行相同范围。Linux 上用firewall-cmd --add-port30000-31000/tcp或 iptables 对应规则云服务器还要在安全组里加同样的端口范围。5.2 上传报 550 Permission denied目录权限没问题也报错现象能登录、能看目录、能下载但只要执行 put 或 STOR 就报 550。原因vsftpd 的write_enableYES没有开启。FTP 服务的写权限由两个层面控制操作系统的目录写权限 FTP 服务本身的写权限开关。很多人只查了前者忽略了后者。解决打开/etc/vsftpd.conf把write_enableYES前面的注释去掉重启 vsftpd。在 Windows 的 FTP 服务器里对应的是用户权限里勾选「写入」。5.3 从外网传文件时速度极慢小文件一秒一个现象同一条宽带下外网从 FTP 下载大文件速度能跑满但一堆小文件传输时速度极慢一个 5KB 的文件要等一秒。原因每个小文件都要走一次完整的控制交互流程RTT 延迟占主导。TCP 连接建立、FTP 指令交互、数据传输、连接关闭一次循环大概要 5 到 8 个 RTT。这和 FTP 机制有关不是网速问题。解决三个方向。一是用 lftp 的--parallel提升并发连接数二是打包后再传把几百个小文件打成 tar.gz 一次性上传三是用 rsync over FTP 不能做可以选择基于 SSH 的方案不过这就超纲了仍然使用 FTP 的话优先打包。5.4 打印机扫描到 FTP 总是失败但电脑上 FTP 完全正常现象美能达、柯美等打印机扫描到 FTP 的时候报连接错误或目录不存在而同一台服务器用电脑 FTP 客户端一切正常。原因打印机这类嵌入式设备的 FTP 客户端实现非常保守对路径空格、中文编码、被动模式支持都不完善。最常见的是路径里有空格或中文设备端做的 URL 编码和解码不一致导致目录切不过去。解决给打印机单独建一个专用目录路径里只用字母、数字、下划线关闭服务器端对 UTF-8 编码的强制要求或者在打印机端把字符集设置为纯 ASCII。还有一点常见故障是打印机不支持被动模式需要在 vsftpd 里开启port_enableYES让主动模式可用。这个问题的排查路径和我处理过的很多「一端好一端坏」故障一致——先检查差异在哪而不是先怀疑协议。5.5 传大文件突然中断后续不能再传现象传一个 5GB 文件传到 80% 断开了再重新传却报 530 Not logged in必须重新登录才能传。原因这是 FTP 服务器的会话超时设置起作用了也可能是 vsftpd 的data_connection_timeout太小。文件传输过程中如果长时间没有数据传输比如网络抖动服务器会主动断开。而且 FTP 的控制连接一旦断开整个会话就作废了。解决调大 vsftpd.conf 里的data_connection_timeout600和idle_session_timeout600单位秒按最大文件传输时间放宽。客户端这边如果使用 lftp设置set net:timeout 60和set net:max-retries 5让它自动重连并断点续传。6. 进阶用法用 FTP 监控与自动化做文件交换的可观测性最后一个进阶技巧解决的是「文件到底传没传成功」这个日常里最容易翻车的环节。FTP 传文件本身不难难的是出了问题你无法快速定位。我给自己的服务器配了一套简单的文件交换监控机制在现有 vsftpd 上不装额外重组件就能实现。6.1 监控 vsftpd 日志并推送到运维群vsftpd 的 xferlog 是现成的数据源。下面这段 cron 脚本每五分钟扫描一次日志解析出最近的新增传输记录有失败就推消息#!/bin/bash # 每分钟检查 vsftpd 日志中的失败记录 LOG_FILE/var/log/vsftpd.log LAST_POS_FILE/tmp/ftp_monitor_pos POS$(cat $LAST_POS_FILE 2/dev/null || echo 0) NEW_POS$(wc -c $LOG_FILE) # 只处理新增日志段避免重复告警 tail -c $((POS1)) $LOG_FILE | grep -E ERROR|FAIL|refused|timed out | while read line; do echo [FTP ALERT] $line # 此处填入你的推送 API比如 webhook done echo $NEW_POS $LAST_POS_FILE思路是用文件字节位置记录上次读到哪里每次只增量读取不重复解析历史日志。grep匹配的同时筛选出错关键字匹配到就发告警。如果故障是偶发性的这个脚本能帮你抢在用户投诉之前发现问题算是一种低配 APM。6.2 定期下载远端文件到本地并校验大小仅监控日志还不够我习惯在脚本同步外加一层大小校验。下面的 Python 脚本把 FTP 下载和 SHA256 校验串在一起解决「下载成功但文件损坏」的场景# -*- coding: utf-8 -*- # 下载远端文件并做 SHA256 校验 from ftplib import FTP import hashlib, os REMOTE_FILE /data/backup/db_backup.zip LOCAL_FILE ./db_backup.zip SERVER 192.168.1.100 USER backup PASSWD Backup2024 def sha256_file(filepath, blocksize65536): sha hashlib.sha256() with open(filepath, rb) as f: for block in iter(lambda: f.read(blocksize), b): sha.update(block) return sha.hexdigest() def download_verify(): ftp FTP() ftp.connect(SERVER, 21, timeout30) ftp.login(USER, PASSWD) ftp.sendcmd(TYPE I) with open(LOCAL_FILE, wb) as f: ftp.retrbinary(fRETR {REMOTE_FILE}, f.write, blocksize131072) ftp.quit() size_ok os.path.getsize(LOCAL_FILE) 0 hash_ok sha256_file(LOCAL_FILE)[:8] # 前 8 位足够用来快速比对 print(fsize_ok{size_ok}, hash_prefix{hash_ok}) return size_ok if __name__ __main__: ok download_verify() exit(0 if ok else 1)这段脚本在实际任务里通常由 crontab 或 systemd timer 触发下载完成后检查退出码非零就告警。这里的hashlib计算完整哈希但在日常运维里只取前 8 位十六进制作为快速指纹已经能挡住绝大多数传输损坏。6.3 为打印机和嵌入式设备单独划分用户和目录这类设备没有交互终端一旦账号权限收得不对排查成本极高。我的经验是给每个设备建独立账号并锁定在其专用目录# 创建设备专用账号并锁定目录 sudo useradd -d /data/ftp/dev1 -s /sbin/nologin dev1 sudo mkdir -p /data/ftp/dev1 sudo chown dev1:dev1 /data/ftp/dev1 sudo chmod 750 /data/ftp/dev1-s /sbin/nologin让这个账号无法登录 shell只能走 FTPchmod 750保证同组可读但其他组不可见。vsftpd 配置里再配合# 为 dev1 设置独立配置目录 user_config_dir/etc/vsftpd_user_conf在/etc/vsftpd_user_conf/dev1里写入local_root/data/ftp/dev1 write_enableYES anon_world_readable_onlyNO这样一来即使设备端的 FTP 客户端写得再差也只能活动在自己的目录里不会串到别的目录排障时只需要看这一个目录的日志而不是翻整个服务器的文件。这套做法我用了几年期间只有一次打印机固件升级后连接方式变了其他时间都不用碰配置。做 FTP 不是在做多高级的事但把上传下载、断点续传、权限隔离、日志监控这几件事串起来就能让文件交换这件事变成基础设施而不是天天救火的临时活。最终判断一个方案值不值得做不看协议新旧看它是否让你一个月少折腾两次。希望这篇笔记帮到你。本文还有配套的精品资源点击获取