2022年 11月 5日

python 实现ftp服务 简介

目录

一. Python搭建FTP服务器

二. FTP函数释义

三. 查看目录结构

四. 上传下载程序


一. Python搭建FTP服务器

1. 搭建FTP服务器的Server端

  1. # -*- coding:utf-8 -*-
  2. from pyftpdlib.authorizers import DummyAuthorizer
  3. from pyftpdlib.handlers import FTPHandler
  4. from pyftpdlib.servers import FTPServer
  5. # 实例化DummyAuthorizer来创建ftp用户
  6. authorizer = DummyAuthorizer()
  7. # 参数:用户名,密码,目录,权限
  8. authorizer.add_user('admin', '123456', r'C:\Users\Administrator\Desktop\ftp', perm='elradfmwMT')
  9. # 匿名登录
  10. # authorizer.add_anonymous('/home/nobody')
  11. handler = FTPHandler
  12. handler.authorizer = authorizer
  13. # 参数:IP,端口,handler
  14. server = FTPServer(('0.0.0.0', 2121), handler) #设置为0.0.0.0为本机的IP地址
  15. server.serve_forever()

2. FTP服务器的客户端连接

  1. # -*- coding: utf-8 -*-
  2. from ftplib import FTP
  3. import time,tarfile,os
  4. #连接ftp
  5. def ftpconnect(host,port, username, password):
  6. ftp = FTP()
  7. # 打开调试级别2,显示详细信息
  8. # ftp.set_debuglevel(2)
  9. ftp.connect(host, port)
  10. ftp.login(username, password)
  11. return ftp
  12. #从ftp下载文件
  13. def downloadfile(ftp, remotepath, localpath):
  14. # 设置的缓冲区大小
  15. bufsize = 1024
  16. fp = open(localpath, 'wb')
  17. ftp.retrbinary('RETR ' + remotepath, fp.write, bufsize)
  18. ftp.set_debuglevel(0)# 参数为0,关闭调试模式
  19. fp.close()
  20. #从本地上传文件到ftp
  21. def uploadfile(ftp, remotepath, localpath):
  22. bufsize = 1024
  23. fp = open(localpath, 'rb')
  24. ftp.storbinary('STOR ' + remotepath, fp, bufsize)
  25. ftp.set_debuglevel(0)
  26. fp.close()
  27. if __name__ == "__main__":
  28. #host,port, username, password
  29. ftp = ftpconnect("192.168.10.113", 2121,"admin", "123456")
  30. #下载文件,第一个是ftp服务器路径下的文件,第二个是要下载到本地的路径文件
  31. downloadfile(ftp, "/12.mp3", r"C:\Users\Administrator\Desktop\ftp\download\test.mp3")
  32. # 上传文件,第一个是要上传到ftp服务器路径下的文件,第二个是本地要上传的的路径文件
  33. uploadfile(ftp, '/upload/1.txt', "C:/Users/Administrator/Desktop/1.txt")
  34. # ftp.close() #关闭ftp
  35. # #调用本地播放器播放下载的视频
  36. # os.system('start D:\soft\kugou\KGMusic\KuGou.exe C:\Users\Administrator\Desktop\ftp\test.mp3')
  37. print(ftp.getwelcome())# 打印出欢迎信息
  38. # 获取当前路径
  39. pwd_path = ftp.pwd()
  40. print("FTP当前路径:", pwd_path)
  41. # 显示目录下所有目录信息
  42. # ftp.dir()
  43. # 设置FTP当前操作的路径
  44. ftp.cwd('/upload/')
  45. # 返回一个文件名列表
  46. filename_list = ftp.nlst()
  47. print(filename_list)
  48. ftp.mkd('目录名')# 新建远程目录
  49. ftp.rmd('目录名') # 删除远程目录
  50. ftp.delete('文件名') # 删除远程文件
  51. ftp.rename('fromname', 'toname') # 将fromname修改名称为toname
  52. # 逐行读取ftp文本文件
  53. file = '/upload/1.txt'
  54. # ftp.retrlines('RETR %s' % file)
  55. #与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8KB)下载的数据
  56. # ftp.retrbinary('RETR %s' % file)

二. FTP函数释义

Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件,函数列举如下

  1. ftp登陆连接
  2. from ftplib import FTP #加载ftp模块
  3. ftp=FTP() #设置变量
  4. ftp.set_debuglevel(2) #打开调试级别2,显示详细信息
  5. ftp.connect("IP","port") #连接的ftp sever和端口
  6. ftp.login("user","password") #连接的用户名,密码
  7. print ftp.getwelcome() #打印出欢迎信息
  8. ftp.cmd("xxx/xxx") #进入远程目录
  9. bufsize=1024 #设置的缓冲区大小
  10. filename="filename.txt" #需要下载的文件
  11. file_handle=open(filename,"wb").write #以写模式在本地打开文件
  12. ftp.retrbinaly("RETR filename.txt",file_handle,bufsize) #接收服务器上文件并写入本地文件
  13. ftp.set_debuglevel(0) #关闭调试模式
  14. ftp.quit() #退出ftp
  15. ftp相关命令操作
  16. ftp.cwd(pathname) #设置FTP当前操作的路径
  17. ftp.dir() #显示目录下所有目录信息
  18. ftp.nlst() #获取目录下的文件
  19. ftp.mkd(pathname) #新建远程目录
  20. ftp.pwd() #返回当前所在位置
  21. ftp.rmd(dirname) #删除远程目录
  22. ftp.delete(filename) #删除远程文件
  23. ftp.rename(fromname, toname)#将fromname修改名称为toname。
  24. ftp.storbinaly("STOR filename.txt",file_handel,bufsize) #上传目标文件
  25. ftp.retrbinary("RETR filename.txt",file_handel,bufsize) #下载FTP文件

FTP.quit()与FTP.close()的区别

FTP.quit():发送QUIT命令给服务器并关闭掉连接。这是一个比较“缓和”的关闭连接方式,但是如果服务器对QUIT命令返回错误时,会抛出异常。

FTP.close():单方面的关闭掉连接,不应该用在已经关闭的连接之后,例如不应用在FTP.quit()之后。

  1. FTP对象方法说明
  2. login(user=’anonymous’,passwd=”, acct=”) 登录 FTP 服务器,所有参数都是可选的
  3. pwd() 获得当前工作目录
  4. cwd(path) 把当前工作目录设置为 path 所示的路径
  5. dir ([path[,…[,cb]]) 显示 path 目录里的内容,可选的参数 cb 是一个回调函数,会传递给 retrlines()方法
  6. nlst ([path[,…]) 与 dir()类似, 但返回一个文件名列表,而不是显示这些文件名
  7. retrlines(cmd [, cb]) 给定 FTP命令(如“ RETR filename”),用于下载文本文件。可选的回调函数 cb 用于处理文件的每一行
  8. retrbinary(cmd,cb[,bs=8192[, ra]]) 与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8KB)下载的数据
  9. storlines(cmd, f) 给定 FTP 命令(如“ STOR filename”),用来上传文本文件。要给定一个文件对象 f
  10. storbinary(cmd, f,[,bs=8192]) 与 storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象 f,上传块大小 bs 默认为 8KB
  11. rename(old, new) 把远程文件 old 重命名为 new
  12. delete(path) 删除位于 path 的远程文件
  13. mkd(directory) 创建远程目录
  14. rmd(directory) 删除远程目录
  15. quit() 关闭连接并退出

 

三. 查看目录结构

ftp.dir() 能显示目录下的文件信息,考虑到要分别对文件夹个数和文件数目进行统计,文件夹下存在文件夹和文件嵌套;将dir()后的目录信息放入列表,对列表进行操作;进入子文件夹后进行递归调用操作。

  1. # -*- coding: utf-8 -*-
  2. from ftplib import FTP
  3. ftp = FTP()
  4. ftp.connect('132.121.xx.xxx', 'xx909')
  5. ftp.login('crmyun_xxx', 'wyjjjjxJ')
  6. sum1 = 0
  7. sum2 = 0
  8. value = 0
  9. def search_file(start_dir):
  10. ftp.cwd(start_dir)
  11. print ftp.pwd()
  12. dir_res = []
  13. ftp.dir('.', dir_res.append) #对当前目录进行dir(),将结果放入列表
  14. for i in dir_res:
  15. if i.startswith("d"):
  16. global sum1
  17. sum1 += 1
  18. search_file(ftp.pwd()+"/"+i.split(" ")[-1])
  19. ftp.cwd('..')
  20. else:
  21. global sum2, value
  22. sum2 += 1
  23. val = i.split(" ")[-1]
  24. value += ftp.size(val)
  25. if ftp.pwd().endswith('/'):
  26. # print ftp.pwd()+val+" "+str(ftp.size(val))+" B" #打印出每个文件路径和大小
  27. pass
  28. else:
  29. # print ftp.pwd()+"/"+val+" "+str(ftp.size(val))+" B"
  30. pass
  31. def sum_file(file_path):
  32. search_file(file_path)
  33. print "folder number is "+str(sum1)+", file number is "+str(sum2)+", Totle size is "+str(value)+" B"
  34. if __name__ == '__main__':
  35. sum_file("/apps/crmyun/crmyun_755")

展示结果:

四. 上传下载程序

完整上传程序

Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件

  1. # coding: utf-8
  2. from ftplib import FTP
  3. import time
  4. import tarfile
  5. import os
  6. # !/usr/bin/python
  7. # -*- coding: utf-8 -*-
  8. from ftplib import FTP
  9. def ftpconnect(host, username, password):
  10. ftp = FTP()
  11. # ftp.set_debuglevel(2)
  12. ftp.connect(host, 21)
  13. ftp.login(username, password)
  14. return ftp
  15. #从ftp下载文件
  16. def downloadfile(ftp, remotepath, localpath):
  17. bufsize = 1024
  18. fp = open(localpath, 'wb')
  19. ftp.retrbinary('RETR ' + remotepath, fp.write, bufsize)
  20. ftp.set_debuglevel(0)
  21. fp.close()
  22. #从本地上传文件到ftp
  23. def uploadfile(ftp, remotepath, localpath):
  24. bufsize = 1024
  25. fp = open(localpath, 'rb')
  26. ftp.storbinary('STOR ' + remotepath, fp, bufsize)
  27. ftp.set_debuglevel(0)
  28. fp.close()
  29. if __name__ == "__main__":
  30. ftp = ftpconnect("113.105.139.xxx", "ftp***", "Guest***")
  31. downloadfile(ftp, "Faint.mp4", "C:/Users/Administrator/Desktop/test.mp4")
  32. #调用本地播放器播放下载的视频
  33. os.system('start "C:\Program Files\Windows Media Player\wmplayer.exe" "C:/Users/Administrator/Desktop/test.mp4"')
  34. uploadfile(ftp, "C:/Users/Administrator/Desktop/test.mp4", "test.mp4")
  35. ftp.quit()

server main 代码:

  1. # _*_ coding:utf-8 _*_
  2. import os, sys, json, hashlib, socketserver, time
  3. base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  4. sys.path.append(base_dir)
  5. from conf import userdb_set
  6. class Ftp_server(socketserver.BaseRequestHandler):
  7. user_home_dir = ''
  8. def auth(self, *args):
  9. '''验证用户名及密码'''
  10. cmd_dic = args[0]
  11. username = cmd_dic["username"]
  12. password = cmd_dic["password"]
  13. f = open(userdb_set.userdb_set(), 'r')
  14. user_info = json.load(f)
  15. if username in user_info.keys():
  16. if password == user_info[username]:
  17. self.request.send('0'.encode())
  18. os.chdir('/home/%s' % username)
  19. self.user_home_dir = os.popen('pwd').read().strip()
  20. data = "%s login successed" % username
  21. self.loging(data)
  22. else:
  23. self.request.send('1'.encode())
  24. data = "%s login failed" % username
  25. self.loging(data)
  26. f.close
  27. else:
  28. self.request.send('1'.encode())
  29. data = "%s login failed" % username
  30. self.loging(data)
  31. f.close
  32. ##########################################
  33. def get(self, *args):
  34. '''给客户端传输文件'''
  35. request_code = {
  36. '0': 'file is ready to get',
  37. '1': 'file not found!'
  38. }
  39. cmd_dic = args[0]
  40. self.loging(json.dumps(cmd_dic))
  41. filename = cmd_dic["filename"]
  42. if os.path.isfile(filename):
  43. self.request.send('0'.encode('utf-8')) # 确认文件存在
  44. self.request.recv(1024)
  45. self.request.send(str(os.stat(filename).st_size).encode('utf-8'))
  46. self.request.recv(1024)
  47. m = hashlib.md5()
  48. f = open(filename, 'rb')
  49. for line in f:
  50. m.update(line)
  51. self.request.send(line)
  52. self.request.send(m.hexdigest().encode('utf-8'))
  53. print('From server:Md5 value has been sended!')
  54. f.close()
  55. else:
  56. self.request.send('1'.encode('utf-8'))
  57. ###########################################
  58. def cd(self, *args):
  59. '''执行cd命令'''
  60. user_current_dir = os.popen('pwd').read().strip()
  61. cmd_dic = args[0]
  62. self.loging(json.dumps(cmd_dic))
  63. path = cmd_dic['path']
  64. if path.startswith('/'):
  65. if self.user_home_dir in path:
  66. os.chdir(path)
  67. new_dir = os.popen('pwd').read()
  68. user_current_dir = new_dir
  69. self.request.send('Change dir successfully!'.encode("utf-8"))
  70. data = 'Change dir successfully!'
  71. self.loging(data)
  72. elif os.path.exists(path):
  73. self.request.send('Permission Denied!'.encode("utf-8"))
  74. data = 'Permission Denied!'
  75. self.loging(data)
  76. else:
  77. self.request.send('Directory not found!'.encode("utf-8"))
  78. data = 'Directory not found!'
  79. self.loging(data)
  80. elif os.path.exists(path):
  81. os.chdir(path)
  82. new_dir = os.popen('pwd').read().strip()
  83. if self.user_home_dir in new_dir:
  84. self.request.send('Change dir successfully!'.encode("utf-8"))
  85. user_current_dir = new_dir
  86. data = 'Change dir successfully!'
  87. self.loging(data)
  88. else:
  89. os.chdir(user_current_dir)
  90. self.request.send('Permission Denied!'.encode("utf-8"))
  91. data = 'Permission Denied!'
  92. self.loging(data)
  93. else:
  94. self.request.send('Directory not found!'.encode("utf-8"))
  95. data = 'Directory not found!'
  96. self.loging(data)
  97. ###########################################
  98. def rm(self, *args):
  99. request_code = {
  100. '0': 'file exist,and Please confirm whether to rm',
  101. '1': 'file not found!'
  102. }
  103. cmd_dic = args[0]
  104. self.loging(json.dumps(cmd_dic))
  105. filename = cmd_dic['filename']
  106. if os.path.exists(filename):
  107. self.request.send('0'.encode("utf-8")) # 确认文件存在
  108. client_response = self.request.recv(1024).decode()
  109. if client_response == '0':
  110. os.popen('rm -rf %s' % filename)
  111. self.request.send(('File %s has been deleted!' % filename).encode("utf-8"))
  112. self.loging('File %s has been deleted!' % filename)
  113. else:
  114. self.request.send(('File %s not deleted!' % filename).encode("utf-8"))
  115. self.loging('File %s not deleted!' % filename)
  116. else:
  117. self.request.send('1'.encode("utf-8"))
  118. ########################################
  119. def pwd(self, *args):
  120. '''执行pwd命令'''
  121. cmd_dic = args[0]
  122. self.loging(json.dumps(cmd_dic))
  123. server_response = os.popen('pwd').read().strip().encode("utf-8")
  124. self.request.send(server_response)
  125. #############################################
  126. def ls(self, *args):
  127. '''执行ls命名'''
  128. cmd_dic = args[0]
  129. self.loging(json.dumps(cmd_dic))
  130. path = cmd_dic['path']
  131. cmd = 'ls -l %s' % path
  132. server_response = os.popen(cmd).read().encode("utf-8")
  133. self.request.send(server_response)
  134. ############################################
  135. def put(self, *args):
  136. '''接收客户端文件'''
  137. cmd_dic = args[0]
  138. self.loging(json.dumps(cmd_dic))
  139. filename = cmd_dic["filename"]
  140. filesize = cmd_dic["size"]
  141. if os.path.isfile(filename):
  142. f = open(filename + '.new', 'wb')
  143. else:
  144. f = open(filename, 'wb')
  145. request_code = {
  146. '200': 'Ready to recceive data!',
  147. '210': 'Not ready to received data!'
  148. }
  149. self.request.send('200'.encode())
  150. receive_size = 0
  151. while True:
  152. if receive_size < filesize:
  153. data = self.request.recv(1024)
  154. f.write(data)
  155. receive_size += len(data)
  156. else:
  157. data = "File %s has been uploaded successfully!" % filename
  158. self.loging(data)
  159. print(data)
  160. break
  161. ################################################
  162. def mkdir(self, *args):
  163. request_code = {
  164. '0': 'Directory has been made!',
  165. '1': 'Directory is aleady exist!'
  166. }
  167. cmd_dic = args[0]
  168. self.loging(json.dumps(cmd_dic))
  169. dir_name = cmd_dic['dir_name']
  170. if os.path.exists(dir_name):
  171. self.request.send('1'.encode("utf-8"))
  172. else:
  173. os.popen('mkdir %s' % dir_name)
  174. self.request.send('0'.encode("utf-8"))
  175. #############################################
  176. def loging(self, data):
  177. '''日志记录'''
  178. localtime = time.asctime(time.localtime(time.time()))
  179. log_file = '/root/ftp/ftpserver/log/server.log'
  180. with open(log_file, 'a', encoding='utf-8') as f:
  181. f.write('%s-->' % localtime + data + '\n')
  182. ##############################################
  183. def handle(self):
  184. # print("您本次访问使用的IP为:%s" %self.client_address[0])
  185. # localtime = time.asctime( time.localtime(time.time()))
  186. # print(localtime)
  187. while True:
  188. try:
  189. self.data = self.request.recv(1024).decode() #
  190. # print(self.data)
  191. cmd_dic = json.loads(self.data)
  192. action = cmd_dic["action"]
  193. # print("用户请求%s"%action)
  194. if hasattr(self, action):
  195. func = getattr(self, action)
  196. func(cmd_dic)
  197. except Exception as e:
  198. self.loging(str(e))
  199. break
  200. def run():
  201. HOST, PORT = '0.0.0.0', 6969
  202. print("The server is started,and listenning at port 6969")
  203. server = socketserver.ThreadingTCPServer((HOST, PORT), Ftp_server)
  204. server.serve_forever()
  205. if __name__ == '__main__':
  206. run()

设置用户口令代码:

  1. #_*_ coding:utf-8 _*_
  2. import os,json,hashlib,sys
  3. base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  4. userdb_file = base_dir+"\data\\userdb"
  5. # print(userdb_file)
  6. def userdb_set():
  7. if os.path.isfile(userdb_file):
  8. # print(userdb_file)
  9. return userdb_file
  10. else:
  11. print('请先为您的服务器创建用户!')
  12. user_data = {}
  13. dict={}
  14. Exit_flags = True
  15. while Exit_flags:
  16. username = input("Please input username:")
  17. if username != 'exit':
  18. password = input("Please input passwod:")
  19. if password != 'exit':
  20. user_data.update({username:password})
  21. m = hashlib.md5()
  22. # m.update('hello')
  23. # print(m.hexdigest())
  24. for i in user_data:
  25. # print(i,user_data[i])
  26. m.update(user_data[i].encode())
  27. dict.update({i:m.hexdigest()})
  28. else:
  29. break
  30. else:
  31. break
  32. f = open(userdb_file,'w')
  33. json.dump(dict,f)
  34. f.close()
  35. 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