用python实现FTP客户端
暑假做的唯一一个有意义的项目吧,其实原理很简单。主要是其中学了不少Ubuntu的东西。今天正好没事,整理一下,以便后面查阅。
首先推荐一个好用的FTP管理程序--IIS7服务器管理工具:
“IIS7服务器管理工具是一款windows全系、Linux系统下链接并操控VPS、VNC、FTP等远程服务器、云服务器。
界面简单明了,操作易上手,功能强大,支持批量导入服务器,并批量打开,多窗口化管理,除此之外,加载本地硬盘、硬盘映射、加载服务器的声音,远程声卡读取等,完全实现各类场景使用,对于FTP链接界面,朋友FTP定时上传,定时下载(也可以说定时上传下载 定时备份),对于经常使用FTP的小伙伴来说,非常适用。
工具支持自动更新,压缩包只有7.62M,方便简洁,一步到位。“
使用说明:
一、 程序说明。
ftp.py程序说明。
程序功能:这个程序主要由三个功能。1.浏览ftp服务器目录。2.随机下载一个文件。3.随机下载多个文件。
1. 浏览ftp服务器目录。
这个功能主要是在登入进ftp服务器后,生成一个随机数,根据随机数,进行判断往哪个目录下索引。如果是根目录,只会向下索引。如果是底层目录,只会向上索引。
2. 随机下载一个文件。
这个功能主要是在登入进ftp服务器后,生成一个随机数,根据随机数,随机下载一个文件。需要注意的是,生成随机数之前,要进行判断。如果没有要下载的文件,就要切换目录,进行下载。
3. 随机多个文件下载
这个功能主要是在登入ftp服务器后,随机下载多个文件。实现原理为,首先进入一个目录下,判断是否有文件下载,如果有文件下载,则下载多个文件,如果没有,则改变目录。完成后改变目录。
所需参数:“ip,port,username,passwd,usermodel”。传入参数由一个字符串组成。用逗号进行分割。
所需环境:需要搭建好ftp服务器。以及有python环境的系统上运行。
注意事项:
1. 下载路径默认为”/home/”。如果程序出错提示为“不能写入”,需要对文件权限进行提升。
2. 在实现用户模式时,程序已经默认多次执行。即每次运行程序时,就会随机多次(至少一次,最多5次)执行,用来模拟用户行为。
3. ftp服务器搭建详见ftp服务器搭建教程。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env Python3 2 # -*- encoding:utf-8 -*- 3 from ftplib import FTP 4 from random import randint 5 import time 6 import sys 7 8 9 10 11 def ftpconnect(ip, port, username, passwd): 12 ftp = FTP() 13 #ftp.set_debuglevel(2) # 打开调试级别2,显示详细信息 14 ftp.connect(ip, port) # 连接的ftp服务器ip和port 15 ftp.login(username, passwd) # 连接的用户名和密码 16 print(ftp.getwelcome()) # 打印出欢迎信息。 17 return ftp 18 19 def showdownloadfile(ftp): 20 #print("当前目录下的文件:文件夹名字:"+ftp.pwd()) 21 mu_dir = gettable(ftp) 22 #print("当前文件夹: "+ftp.pwd()+" 可供下载的文件:") 23 behind_name = [] 24 for name in mu_dir: 25 if mu_dir[name] == "文件": 26 behind_name.append(name) 27 #print(name) 28 return behind_name 29 30 def showfolderfile(ftp): 31 #print("当前目录下的文件:文件夹名字:"+ftp.pwd()) 32 mu_dir = gettable(ftp) 33 #print("当前文件夹: "+ftp.pwd()+" 可供索引的文件夹:") 34 folder_name = [] 35 for name in mu_dir: 36 if mu_dir[name] == "文件夹": 37 folder_name.append(name) 38 #print(name) 39 return folder_name 40 41 def randomdownload(ftp, localpath, str): 42 # 当前目录下可供下载的文件名字 43 behind_name = showdownloadfile(ftp) 44 #print("当前文件夹: "+ftp.pwd()+" 可供下载的文件:") 45 #print(behind_name) 46 # 随机数 47 while len(behind_name) == 0: # 说明没有可以下载的文件 48 # 切换文件夹 49 changetable(ftp,str) 50 behind_name = showdownloadfile(ftp) 51 file_random = randint(0, len(behind_name)-1) 52 filename = behind_name[file_random] 53 print("当前目录为: "+ftp.pwd()) 54 print("下载文件为 "+filename) 55 # 设置下载数据 56 bufsize = 1024 # 设置缓冲区大小 57 new_path = localpath+filename # 设置下载路径 58 59 fp = open(new_path, 'wb') 60 ftp.retrbinary('RETR ' + filename, fp.write, bufsize) # 下载FTP文件。FTP命令 RETR filename 61 fp.close() 62 print(filename+' 下载成功') 63 64 65 def downloadfiles(ftp, localpath, str): # 根目录不能继续向下,没有目录不能索引(这个可以自动实现)。 66 # 在当前目录下随机下载几个文件 67 behind_name = showdownloadfile(ftp) 68 #folder_name = showfolderfile(ftp) 69 while len(behind_name) == 0: # 说明没有可以下载的文件 70 # 切换文件夹 71 changetable(ftp, str) 72 behind_name = showdownloadfile(ftp) 73 74 randomb = randint(0, len(behind_name)-1) 75 while randomb: # 在这个文件夹下随机下载几个 76 randomdownload(ftp, localpath, str) 77 randomb -= 1 78 # 切换目录下载(可能向下或者向上) 79 #viewtable(ftp) 80 81 82 83 84 def gettable(ftp): # 获取目录结构 85 dir_del = [] 86 87 table_dir = {} 88 ftp.dir('.', dir_del.append) # 根据文件目录,进行拆分。 89 for name in dir_del: 90 strname = str(name) 91 temp = strname.split(' ') 92 attrname = [] 93 for j in range(0,len(temp)): 94 if temp[j] != '': 95 attrname.append(temp[j]) 96 m = ' '.join(attrname[8:]) 97 if strname.startswith('d'): 98 #print('文件夹:', m) 99 table_dir[m] = '文件夹' 100 else: 101 #print('文件:', m) 102 table_dir[m] = '文件' 103 return table_dir # 字典,key值是文件名,value是类型 104 105 def viewtable(ftp,str): 106 #gettable(ftp) # 获取当前目录 107 changtimes = randint(1, 4) # 随机切换几次目录 108 while changtimes: 109 changtimes -= 1 110 folder_name = showfolderfile(ftp) 111 print("当前目录下为: "+ftp.pwd()) 112 print("目录结构:") 113 print(gettable(ftp)) 114 if len(folder_name) > 0: # 有可供索引的文件夹 115 if ftp.pwd() == str: # 为根目录,只能向下索引 116 print("向下索引后目录为:") 117 randomf = randint(0, len(folder_name)-1) 118 path = ftp.pwd()+folder_name[randomf] 119 ftp.cwd(path) 120 print("当前目录下为: "+ftp.pwd()) 121 print("目录结构:") 122 print(gettable(ftp)) 123 124 else: # 向上向下索引都可以 125 downorup = randint(0, 1) 126 if downorup: # 向下索引 127 print("向下索引后目录为:") 128 randomf = randint(0, len(folder_name)-1) 129 path = ftp.pwd()+folder_name[randomf] 130 ftp.cwd(path) 131 print("当前目录下为: "+ftp.pwd()) 132 print("目录结构:") 133 print(gettable(ftp)) 134 135 else: 136 print("向上索引后,目录为:") 137 path = '../' 138 ftp.cwd(path) 139 print("当前目录下为: "+ftp.pwd()) 140 print("目录结构:") 141 print(gettable(ftp)) 142 else: # 没有可供索引的文件夹,可以向上索引。 143 if ftp.pwd() != str: 144 print("向上索引后,目录为:") 145 path = '../' 146 ftp.cwd(path) 147 print("当前目录下为: "+ftp.pwd()) 148 print("目录结构:") 149 print(gettable(ftp)) 150 151 def changetable(ftp,str): 152 folder_name = showfolderfile(ftp) 153 if len(folder_name) > 0: # 有可供索引的文件夹 154 if ftp.pwd() == str: # 为根目录,只能向下索引 155 randomf = randint(0, len(folder_name)-1) 156 path = ftp.pwd()+folder_name[randomf] 157 ftp.cwd(path) 158 159 else: # 向上向下索引都可以 160 downorup = randint(0, 1) 161 if downorup: # 向下索引 162 randomf = randint(0, len(folder_name)-1) 163 path = ftp.pwd()+folder_name[randomf] 164 ftp.cwd(path) 165 166 else: 167 path = '../' 168 ftp.cwd(path) 169 else: # 没有可供索引的文件夹,可以向上索引。 170 if ftp.pwd() != str: 171 path = '../' 172 ftp.cwd(path) 173 def ftpmain(ip, port, username, passwd, localpath, userModel): 174 # 1.连接到ftp服务器 175 ftp = ftpconnect(ip, port, username, passwd) 176 # 2.开始交互 177 str = ftp.pwd() 178 #print(str) 179 # usermodel 也可以是一个列表序列,让一个用户,执行多个操作。 180 cmdtimes = randint(1, 5) # 这随机多次执行用户模式。 181 #downloadtimes = 3 # 多个文件多次下载 182 while cmdtimes: 183 cmdtimes -= 1 184 if userModel == 1: # 浏览模式 185 viewtable(ftp, str) 186 changetable(ftp, str) 187 continue 188 if userModel == 2: # 下载模式 189 randomdownload(ftp, localpath, str) 190 changetable(ftp, str) 191 continue 192 if userModel == 3: # 同时下载多个文件模式 193 #while downloadtimes: 194 downloadfiles(ftp, localpath, str) 195 changetable(ftp, str) 196 #downloadtimes -= 1 197 continue 198 else: # 错误处理机制 199 print('无法识别传递的模式编号:') 200 break 201 202 ftp.quit() 203 204 205 if __name__ == "__main__": 206 agrvlist = sys.argv[1].split(',') 207 ip = agrvlist[0] 208 port = int(agrvlist[1]) 209 username = agrvlist[2] 210 passwd = agrvlist[3] 211 usermodel = int(agrvlist[4]) 212 ftpmain(ip, port, username, passwd, "/home/username/", usermodel)
从程序中我们可以看出,其实还是很简单的。主要是引入ftplib库。用pip就可以。
下面简单说一下,我在编程中主要遇到的问题:
1.区分ftp服务器里面的文件夹和文件。
这里是解决的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def gettable(ftp): # 获取目录结构 2 dir_del = [] 3 4 table_dir = {} 5 ftp.dir('.', dir_del.append) # 根据文件目录,进行拆分。 6 for name in dir_del: 7 strname = str(name) 8 temp = strname.split(' ') 9 attrname = [] 10 for j in range(0,len(temp)): 11 if temp[j] != '': 12 attrname.append(temp[j]) 13 m = ' '.join(attrname[8:]) 14 if strname.startswith('d'): 15 #print('文件夹:', m) 16 table_dir[m] = '文件夹' 17 else: 18 #print('文件:', m) 19 table_dir[m] = '文件' 20 return table_dir # 字典,key值是文件名,value是类型
主要是先获取服务器文件夹里面的详细信息,通过分析,利用python的数据结构来区分。
下面是我的分析过程:
这是ftp服务器主目录下,通过ftp.dir(),然后输出结果为:
['drwxrwxrwx 2 0 0 4096 Jul 04 21:17 hello', '-rw------- 1 1001 131 3 Jul 05 10:20 test', '-rwxrwxrwx 1 1000 1000 2840284 Apr 20 2016 test.ogg']
通过查阅发现:hello是文件夹,test和test.ogg是文件。
然后进一步查分,最后通过'd'来区分。
strname.startswith('d')
2.获取外部输入
这个程序不是直接编译通过的,而是要传递外部参数。利用sys库进行操作。通过控制台运行函数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 if __name__ == "__main__": 2 agrvlist = sys.argv[1].split(',') 3 ip = agrvlist[0] 4 port = int(agrvlist[1]) 5 username = agrvlist[2] 6 passwd = agrvlist[3] 7 usermodel = int(agrvlist[4])
由于 外部参数都是以字符串形式传入,所以要进行格式转换操作。
3.限制下载速度。
这个功能在程序中没有实现。后来通过阅读关于网络的书籍,关于tcp的过程,其实是可以通过设置缓冲区的大小来模拟延迟的。当然这个下载速度服务器是由主导权的。
这个项目主要学到的东西在ftp服务器搭建中,放到下个随笔中。