使用Python ftplib上传文件至FTP服务器时遇Connection refused错误
问题排查与解决方案
我仔细看了你的代码、错误回溯和FileZilla日志,发现两个关键问题,其中第二个是导致ConnectionRefusedError的核心原因:
1. 函数内部的变量引用错误
你的ftp_upload函数定义了参数ftp_obj,但函数内部却直接调用了全局的ftp变量,这是一个明显的bug。虽然在当前代码结构下可能暂时能运行,但这是不良编码习惯,后续复用函数时会引发问题。
修正方法:把函数内的ftp.storlines和ftp.storbinary改成ftp_obj.storlines和ftp_obj.storbinary。
2. FTP主动模式被环境阻止(核心原因)
从FileZilla的日志可以看到,服务器返回了:
200 PORT command successful. Consider using PASV.
这说明服务器支持被动模式(PASV),并且推荐使用这种模式。而Python的ftplib.FTP默认使用主动模式(PORT),在这种模式下,当执行文件传输命令(比如STOR)时,FTP服务器需要主动发起连接到你的客户端端口,这个连接很可能被你的本地防火墙、路由器NAT规则或者服务器端的防火墙拦截,从而导致ConnectionRefusedError。
解决方法:在登录FTP服务器后,调用ftp.set_pasv(True)切换到被动模式。
修正后的完整代码
import ftplib def ftp_upload(ftp_obj, path, ftype='TXT'): """ A function for uploading files to an FTP server @param ftp_obj: The file transfer protocol object @param path: The path to the file to upload """ # 提取本地文件名,避免把本地路径当成远程路径 filename = path.split('/')[-1] if ftype == 'TXT': with open(path) as fobj: # 修正:使用传入的ftp_obj而非全局ftp ftp_obj.storlines(f'STOR {filename}', fobj) else: with open(path, 'rb') as fobj: # 修正:使用传入的ftp_obj而非全局ftp ftp_obj.storbinary(f'STOR {filename}', fobj, 1024) if __name__ == '__main__': ftp = ftplib.FTP(host='domain or i.p. address') ftp.login(user='username', passwd='password') # 新增:切换到被动模式,解决连接被拒绝问题 ftp.set_pasv(True) path = input('Please provide path to file: ') ftp_upload(ftp, path) pdf_path = '/path/to/something.pdf' ftp_upload(ftp, pdf_path, ftype='PDF') ftp.quit()
额外优化说明:我还修改了STOR命令后的路径,只传入文件名(用path.split('/')[-1]提取),避免把本地的完整路径当成远程服务器上的路径,这样文件会直接上传到当前登录的目录下,和FileZilla的行为保持一致。
后续验证思路
如果修改后仍然有问题,可以尝试:
- 确认服务器的被动模式端口范围(通常是1024-65535)是否在服务器端防火墙开放
- 检查本地防火墙是否允许Python程序发起出站连接
- 尝试升级Python版本(你的Python是3.6,虽然版本较老,但核心逻辑没问题,新版本可能有更完善的FTP兼容处理)
内容的提问来源于stack exchange,提问作者EliC




