本文从以下三个方面, 阐述Python如何搭建FTP服务器
一. Python搭建FTP服务器
二. FTP函数释义
三. 查看目录结构
四. 上传下载程序
一. Python搭建FTP服务器
1. 搭建FTP服务器的Server端
# -*- coding:utf-8 -*- from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer # 实例化DummyAuthorizer来创建ftp用户 authorizer = DummyAuthorizer() # 参数:用户名,密码,目录,权限 authorizer.add_user(\'admin\', \'123456\', r\'C:\Users\Administrator\Desktop\ftp\', perm=\'elradfmwMT\') # 匿名登录 # authorizer.add_anonymous(\'/home/nobody\') handler = FTPHandler handler.authorizer = authorizer # 参数:IP,端口,handler server = FTPServer((\'0.0.0.0\', 2121), handler) #设置为0.0.0.0为本机的IP地址 server.serve_forever()
2. FTP服务器的客户端连接
# -*- coding: utf-8 -*- from ftplib import FTP import time,tarfile,os #连接ftp def ftpconnect(host,port, username, password): ftp = FTP() # 打开调试级别2,显示详细信息 # ftp.set_debuglevel(2) ftp.connect(host, port) ftp.login(username, password) return ftp #从ftp下载文件 def downloadfile(ftp, remotepath, localpath): # 设置的缓冲区大小 bufsize = 1024 fp = open(localpath, \'wb\') ftp.retrbinary(\'RETR \' + remotepath, fp.write, bufsize) ftp.set_debuglevel(0)# 参数为0,关闭调试模式 fp.close() #从本地上传文件到ftp def uploadfile(ftp, remotepath, localpath): bufsize = 1024 fp = open(localpath, \'rb\') ftp.storbinary(\'STOR \' + remotepath, fp, bufsize) ftp.set_debuglevel(0) fp.close() if __name__ == "__main__": #host,port, username, password ftp = ftpconnect("192.168.10.113", 2121,"admin", "123456") #下载文件,第一个是ftp服务器路径下的文件,第二个是要下载到本地的路径文件 downloadfile(ftp, "/12.mp3", r"C:\Users\Administrator\Desktop\ftp\download\test.mp3") # 上传文件,第一个是要上传到ftp服务器路径下的文件,第二个是本地要上传的的路径文件 uploadfile(ftp, \'/upload/1.txt\', "C:/Users/Administrator/Desktop/1.txt") # ftp.close() #关闭ftp # #调用本地播放器播放下载的视频 # os.system(\'start D:\soft\kugou\KGMusic\KuGou.exe C:\Users\Administrator\Desktop\ftp\test.mp3\') print(ftp.getwelcome())# 打印出欢迎信息 # 获取当前路径 pwd_path = ftp.pwd() print("FTP当前路径:", pwd_path) # 显示目录下所有目录信息 # ftp.dir() # 设置FTP当前操作的路径 ftp.cwd(\'/upload/\') # 返回一个文件名列表 filename_list = ftp.nlst() print(filename_list) ftp.mkd(\'目录名\')# 新建远程目录 ftp.rmd(\'目录名\') # 删除远程目录 ftp.delete(\'文件名\') # 删除远程文件 ftp.rename(\'fromname\', \'toname\') # 将fromname修改名称为toname # 逐行读取ftp文本文件 file = \'/upload/1.txt\' # ftp.retrlines(\'RETR %s\' % file) #与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8KB)下载的数据 # ftp.retrbinary(\'RETR %s\' % file)
二. FTP函数释义
Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件,函数列举如下
ftp登陆连接 from ftplib import FTP #加载ftp模块 ftp=FTP() #设置变量 ftp.set_debuglevel(2) #打开调试级别2,显示详细信息 ftp.connect("IP","port") #连接的ftp sever和端口 ftp.login("user","password") #连接的用户名,密码 print ftp.getwelcome() #打印出欢迎信息 ftp.cmd("xxx/xxx") #进入远程目录 bufsize=1024 #设置的缓冲区大小 filename="filename.txt" #需要下载的文件 file_handle=open(filename,"wb").write #以写模式在本地打开文件 ftp.retrbinaly("RETR filename.txt",file_handle,bufsize) #接收服务器上文件并写入本地文件 ftp.set_debuglevel(0) #关闭调试模式 ftp.quit() #退出ftp ftp相关命令操作 ftp.cwd(pathname) #设置FTP当前操作的路径 ftp.dir() #显示目录下所有目录信息 ftp.nlst() #获取目录下的文件 ftp.mkd(pathname) #新建远程目录 ftp.pwd() #返回当前所在位置 ftp.rmd(dirname) #删除远程目录 ftp.delete(filename) #删除远程文件 ftp.rename(fromname, toname)#将fromname修改名称为toname。 ftp.storbinaly("STOR filename.txt",file_handel,bufsize) #上传目标文件 ftp.retrbinary("RETR filename.txt",file_handel,bufsize) #下载FTP文件
FTP.quit()与FTP.close()的区别
FTP.quit():发送QUIT命令给服务器并关闭掉连接。这是一个比较“缓和”的关闭连接方式,但是如果服务器对QUIT命令返回错误时,会抛出异常。
FTP.close():单方面的关闭掉连接,不应该用在已经关闭的连接之后,例如不应用在FTP.quit()之后。
FTP对象方法说明 login(user=’anonymous’,passwd=”, acct=”) 登录 FTP 服务器,所有参数都是可选的 pwd() 获得当前工作目录 cwd(path) 把当前工作目录设置为 path 所示的路径 dir ([path[,…[,cb]]) 显示 path 目录里的内容,可选的参数 cb 是一个回调函数,会传递给 retrlines()方法 nlst ([path[,…]) 与 dir()类似, 但返回一个文件名列表,而不是显示这些文件名 retrlines(cmd [, cb]) 给定 FTP命令(如“ RETR filename”),用于下载文本文件。可选的回调函数 cb 用于处理文件的每一行 retrbinary(cmd,cb[,bs=8192[, ra]]) 与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8KB)下载的数据 storlines(cmd, f) 给定 FTP 命令(如“ STOR filename”),用来上传文本文件。要给定一个文件对象 f storbinary(cmd, f,[,bs=8192]) 与 storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象 f,上传块大小 bs 默认为 8KB rename(old, new) 把远程文件 old 重命名为 new delete(path) 删除位于 path 的远程文件 mkd(directory) 创建远程目录 rmd(directory) 删除远程目录 quit() 关闭连接并退出
三. 查看目录结构
ftp.dir() 能显示目录下的文件信息,考虑到要分别对文件夹个数和文件数目进行统计,文件夹下存在文件夹和文件嵌套;将dir()后的目录信息放入列表,对列表进行操作;进入子文件夹后进行递归调用操作。
# -*- coding: utf-8 -*- from ftplib import FTP ftp = FTP() ftp.connect(\'132.121.xx.xxx\', \'xx909\') ftp.login(\'crmyun_xxx\', \'wyjjjjxJ\') sum1 = 0 sum2 = 0 value = 0 def search_file(start_dir): ftp.cwd(start_dir) print ftp.pwd() dir_res = [] ftp.dir(\'.\', dir_res.append) #对当前目录进行dir(),将结果放入列表 for i in dir_res: if i.startswith("d"): global sum1 sum1 += 1 search_file(ftp.pwd()+"/"+i.split(" ")[-1]) ftp.cwd(\'..\') else: global sum2, value sum2 += 1 val = i.split(" ")[-1] value += ftp.size(val) if ftp.pwd().endswith(\'/\'): # print ftp.pwd()+val+" "+str(ftp.size(val))+" B" #打印出每个文件路径和大小 pass else: # print ftp.pwd()+"/"+val+" "+str(ftp.size(val))+" B" pass def sum_file(file_path): search_file(file_path) print "folder number is "+str(sum1)+", file number is "+str(sum2)+", Totle size is "+str(value)+" B" if __name__ == \'__main__\': sum_file("/apps/crmyun/crmyun_755")
展示结果:
四. 上传下载程序
完整上传程序
Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件
# coding: utf-8 from ftplib import FTP import time import tarfile import os # !/usr/bin/python # -*- coding: utf-8 -*- from ftplib import FTP def ftpconnect(host, username, password): ftp = FTP() # ftp.set_debuglevel(2) ftp.connect(host, 21) ftp.login(username, password) return ftp #从ftp下载文件 def downloadfile(ftp, remotepath, localpath): bufsize = 1024 fp = open(localpath, \'wb\') ftp.retrbinary(\'RETR \' + remotepath, fp.write, bufsize) ftp.set_debuglevel(0) fp.close() #从本地上传文件到ftp def uploadfile(ftp, remotepath, localpath): bufsize = 1024 fp = open(localpath, \'rb\') ftp.storbinary(\'STOR \' + remotepath, fp, bufsize) ftp.set_debuglevel(0) fp.close() if __name__ == "__main__": ftp = ftpconnect("113.105.139.xxx", "ftp***", "Guest***") downloadfile(ftp, "Faint.mp4", "C:/Users/Administrator/Desktop/test.mp4") #调用本地播放器播放下载的视频 os.system(\'start "C:\Program Files\Windows Media Player\wmplayer.exe" "C:/Users/Administrator/Desktop/test.mp4"\') uploadfile(ftp, "C:/Users/Administrator/Desktop/test.mp4", "test.mp4") ftp.quit()
server main 代码:
# _*_ coding:utf-8 _*_ import os, sys, json, hashlib, socketserver, time base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from conf import userdb_set class Ftp_server(socketserver.BaseRequestHandler): user_home_dir = \'\' def auth(self, *args): \'\'\'验证用户名及密码\'\'\' cmd_dic = args[0] username = cmd_dic["username"] password = cmd_dic["password"] f = open(userdb_set.userdb_set(), \'r\') user_info = json.load(f) if username in user_info.keys(): if password == user_info[username]: self.request.send(\'0\'.encode()) os.chdir(\'/home/%s\' % username) self.user_home_dir = os.popen(\'pwd\').read().strip() data = "%s login successed" % username self.loging(data) else: self.request.send(\'1\'.encode()) data = "%s login failed" % username self.loging(data) f.close else: self.request.send(\'1\'.encode()) data = "%s login failed" % username self.loging(data) f.close ########################################## def get(self, *args): \'\'\'给客户端传输文件\'\'\' request_code = { \'0\': \'file is ready to get\', \'1\': \'file not found!\' } cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) filename = cmd_dic["filename"] if os.path.isfile(filename): self.request.send(\'0\'.encode(\'utf-8\')) # 确认文件存在 self.request.recv(1024) self.request.send(str(os.stat(filename).st_size).encode(\'utf-8\')) self.request.recv(1024) m = hashlib.md5() f = open(filename, \'rb\') for line in f: m.update(line) self.request.send(line) self.request.send(m.hexdigest().encode(\'utf-8\')) print(\'From server:Md5 value has been sended!\') f.close() else: self.request.send(\'1\'.encode(\'utf-8\')) ########################################### def cd(self, *args): \'\'\'执行cd命令\'\'\' user_current_dir = os.popen(\'pwd\').read().strip() cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) path = cmd_dic[\'path\'] if path.startswith(\'/\'): if self.user_home_dir in path: os.chdir(path) new_dir = os.popen(\'pwd\').read() user_current_dir = new_dir self.request.send(\'Change dir successfully!\'.encode("utf-8")) data = \'Change dir successfully!\' self.loging(data) elif os.path.exists(path): self.request.send(\'Permission Denied!\'.encode("utf-8")) data = \'Permission Denied!\' self.loging(data) else: self.request.send(\'Directory not found!\'.encode("utf-8")) data = \'Directory not found!\' self.loging(data) elif os.path.exists(path): os.chdir(path) new_dir = os.popen(\'pwd\').read().strip() if self.user_home_dir in new_dir: self.request.send(\'Change dir successfully!\'.encode("utf-8")) user_current_dir = new_dir data = \'Change dir successfully!\' self.loging(data) else: os.chdir(user_current_dir) self.request.send(\'Permission Denied!\'.encode("utf-8")) data = \'Permission Denied!\' self.loging(data) else: self.request.send(\'Directory not found!\'.encode("utf-8")) data = \'Directory not found!\' self.loging(data) ########################################### def rm(self, *args): request_code = { \'0\': \'file exist,and Please confirm whether to rm\', \'1\': \'file not found!\' } cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) filename = cmd_dic[\'filename\'] if os.path.exists(filename): self.request.send(\'0\'.encode("utf-8")) # 确认文件存在 client_response = self.request.recv(1024).decode() if client_response == \'0\': os.popen(\'rm -rf %s\' % filename) self.request.send((\'File %s has been deleted!\' % filename).encode("utf-8")) self.loging(\'File %s has been deleted!\' % filename) else: self.request.send((\'File %s not deleted!\' % filename).encode("utf-8")) self.loging(\'File %s not deleted!\' % filename) else: self.request.send(\'1\'.encode("utf-8")) ######################################## def pwd(self, *args): \'\'\'执行pwd命令\'\'\' cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) server_response = os.popen(\'pwd\').read().strip().encode("utf-8") self.request.send(server_response) ############################################# def ls(self, *args): \'\'\'执行ls命名\'\'\' cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) path = cmd_dic[\'path\'] cmd = \'ls -l %s\' % path server_response = os.popen(cmd).read().encode("utf-8") self.request.send(server_response) ############################################ def put(self, *args): \'\'\'接收客户端文件\'\'\' cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) filename = cmd_dic["filename"] filesize = cmd_dic["size"] if os.path.isfile(filename): f = open(filename + \'.new\', \'wb\') else: f = open(filename, \'wb\') request_code = { \'200\': \'Ready to recceive data!\', \'210\': \'Not ready to received data!\' } self.request.send(\'200\'.encode()) receive_size = 0 while True: if receive_size < filesize: data = self.request.recv(1024) f.write(data) receive_size += len(data) else: data = "File %s has been uploaded successfully!" % filename self.loging(data) print(data) break ################################################ def mkdir(self, *args): request_code = { \'0\': \'Directory has been made!\', \'1\': \'Directory is aleady exist!\' } cmd_dic = args[0] self.loging(json.dumps(cmd_dic)) dir_name = cmd_dic[\'dir_name\'] if os.path.exists(dir_name): self.request.send(\'1\'.encode("utf-8")) else: os.popen(\'mkdir %s\' % dir_name) self.request.send(\'0\'.encode("utf-8")) ############################################# def loging(self, data): \'\'\'日志记录\'\'\' localtime = time.asctime(time.localtime(time.time())) log_file = \'/root/ftp/ftpserver/log/server.log\' with open(log_file, \'a\', encoding=\'utf-8\') as f: f.write(\'%s-->\' % localtime + data + \'\n\') ############################################## def handle(self): # print("您本次访问使用的IP为:%s" %self.client_address[0]) # localtime = time.asctime( time.localtime(time.time())) # print(localtime) while True: try: self.data = self.request.recv(1024).decode() # # print(self.data) cmd_dic = json.loads(self.data) action = cmd_dic["action"] # print("用户请求%s"%action) if hasattr(self, action): func = getattr(self, action) func(cmd_dic) except Exception as e: self.loging(str(e)) break def run(): HOST, PORT = \'0.0.0.0\', 6969 print("The server is started,and listenning at port 6969") server = socketserver.ThreadingTCPServer((HOST, PORT), Ftp_server) server.serve_forever() if __name__ == \'__main__\': run()
设置用户口令代码:
#_*_ coding:utf-8 _*_ import os,json,hashlib,sys base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) userdb_file = base_dir+"\data\\userdb" # print(userdb_file) def userdb_set(): if os.path.isfile(userdb_file): # print(userdb_file) return userdb_file else: print(\'请先为您的服务器创建用户!\') user_data = {} dict={} Exit_flags = True while Exit_flags: username = input("Please input username:") if username != \'exit\': password = input("Please input passwod:") if password != \'exit\': user_data.update({username:password}) m = hashlib.md5() # m.update(\'hello\') # print(m.hexdigest()) for i in user_data: # print(i,user_data[i]) m.update(user_data[i].encode()) dict.update({i:m.hexdigest()}) else: break else: break f = open(userdb_file,\'w\') json.dump(dict,f) f.close() return userdb_file
参考文章:
python的ftplib包介绍:https://docs.python.org/3/library/ftplib.html
https://blog.csdn.net/xc_zhou/article/details/81021414
https://www.jb51.net/article/142388.htm
https://www.jb51.net/article/109429.htm
https://www.cnblogs.com/hltswd/p/6228992.html