一坨需求...

  1. 用户加密认证
  2. 允许同时多用户登录
  3. 每个用户有自己的家目录 ,且只能访问自己的家目录
  4. 对用户进行磁盘配额,每个用户的可用空间不同
  5. 允许用户在ftp server上随意切换目录 (cd)
  6. 允许用户查看当前目录下文件 (ls)
  7. 允许上传和下载文件,保证文件一致性(get put)
  8. 文件传输过程中显示进度条
  9. 支持文件的断点续传

程序实现

1、README

  1. ### 作者信息
  2. 姓名: hexm
  3. email: xiaoming.unix@gmail.com
  4.  
  5. ### 实现功能
  6. 用户加密认证
  7. 允许同时多用户登录
  8. 每个用户有自己的家目录 ,且只能访问自己的家目录
  9. 对用户进行磁盘配额,每个用户的可用空间不同
  10. 允许用户在ftp server上随意切换目录 (cd)
  11. 允许用户查看当前目录下文件 (ls)
  12. 允许上传和下载文件,保证文件一致性(get put)
  13. 文件传输过程中显示进度条
  14. 支持文件的断点续传
  15.  
  16. ### 代码目录树
  17. ftpserver/
  18. ├── bin
  19.    └── ftpserver.py
  20. ├── client
  21.    └── ftp.py
  22. ├── db
  23.    └── user.db
  24. ├── fstab
  25. ├── inittab
  26. ├── lib
  27.    ├── common.py
  28.    ├── __pycache__
  29.       ├── common.cpython-35.pyc
  30.       └── user_lib.cpython-35.pyc
  31.    └── user_lib.py
  32. ├── src
  33.    ├── ftpserver.py
  34.    └── __pycache__
  35.    └── ftpserver.cpython-35.pyc
  36. └── yum.conf
  37.  
  38. ### 功能介绍
  39. 默认有一个用户,hexm,密码也是hexm,家目录为/tmp,限额1M,
  40. pwd 查看用户家目录
  41. cd 进入家目录其他目录,并且不能进入其他目录
  42. ls 列出家目录下内容
  43. put 上传文件 支持断点续传
  44. get 下载文件 支持断点续传
  45. bye 退出
  46.  
  47. ### 操作步骤
  48. * 启动服务端
  49. python3 ftpserver/bin/ftpserver.py &
  50. * 启动客户端
  51. python3 ftpserver/client/ftp.py hexm@127.0.0.1 21
  52. >> 输入ftp密码:hexm
  53. 认证成功
  54.  
  55. * ls 列出当前目录内容
  56. 列出当前目录内容
  57. ftp> ls
  58. etc/ fstab inittab pip-53mpg1m2-unpack/ pip-poq_f3ob-unpack/ yum.conf
  59. 列出某一目录内容
  60. ftp> ls etc
  61. fstab group inittab
  62. ftp> ls /etc
  63. fstab group inittab
  64. 显示多个文件详细信息
  65. ftp> ls -l yum.conf fstab
  66. -rw-r--r-- 1 root root 970 11 20 11:46 /tmp/yum.conf
  67. -rw-r--r-- 1 root root 396 11 19 19:44 /tmp/fstab
  68.  
  69. * cd 进入其他目录
  70. 查看当前目录
  71. ftp> pwd
  72. /tmp
  73. 进入家目录中/etc目录
  74. ftp> cd /etc
  75. ftp> pwd
  76. /tmp/etc
  77. 返回上级目录
  78. ftp> cd ..
  79. ftp> pwd
  80. /tmp
  81.  
  82. * 上传文件 支持断点续传
  83. 上传bash文件,因为家目录限额为1M,不能上传
  84. ftp> put /bin/bash
  85. 磁盘空间不足
  86. 上传文件不存在
  87. ftp> put /bin/ifstat
  88. 文件不存在
  89. 正常上传
  90. ftp> put /etc/fstab
  91. 开始上传[/etc/fstab]
  92. ====================================================================================================>100%
  93.  
  94. * 下载文件,支持断点续传
  95. 发送并校验成功
  96. ftp> get yum.conf
  97. ====================================================================================================>100%
  98. * 退出
  99. ftp> bye

2、目录树

3、bin/ftpserver.py

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3.  
  4. import os
  5. import sys
  6. sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  7. from src import ftpserver as src_server
  8.  
  9. if __name__ == '__main__':
  10. src_server.run()

4、src/ftpserver.py

  1. #!/bin/python
  2. # coding=utf-8
  3.  
  4. import socketserver
  5. import json
  6. import os
  7. import subprocess
  8. import re
  9.  
  10. os.sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  11. from lib.user_lib import User
  12. from lib.common import bytes_dumps, str_loads, bytes_encoding, str_encoding, getdirsize
  13.  
  14. class FtpServer(socketserver.BaseRequestHandler):
  15.  
  16. def handle(self):
  17. self.count = 0
  18. while True:
  19. recv_data = self.request.recv(1024)
  20. if len(recv_data) == 0: break
  21. task_data = str_loads(recv_data)
  22. task_action = task_data.get('action')
  23. # 根据客户端命令执行操作
  24. if hasattr(self, task_action):
  25. func = getattr(self, task_action)
  26. func(task_data)
  27.  
  28. def auth(self, *args, **kwargs):
  29. """
  30. 验证用户合法性
  31. param auth_user: 用户名
  32. param auth_pwd: 密码
  33. param ret: 是否验证成功, 结果为True或False
  34. param self.home_path: 用户家目录
  35. param auth_pass_msg: 发送给客户端是否验证成功的信息
  36. """
  37. auth_user = args[0].get('user')
  38. auth_pwd = args[0].get('pwd')
  39. auth = User(auth_user, auth_pwd)
  40. ret = auth.login()
  41. if ret: # 认证成功获取用户家目录
  42. auth_pass_msg = {"status": "True"}
  43. self.home_path = auth.home_path()
  44. self.limit_home = auth.limit_home()
  45. else:
  46. auth_pass_msg = {"status": "False"}
  47. self.request.send(bytes_dumps(auth_pass_msg))
  48.  
  49. def put(self, *args, **kwargs):
  50. file_size = args[0].get('file_size')
  51. file_name = args[0].get('file_name')
  52. md5sum = args[0].get('md5sum')
  53. abs_file = os.path.join(self.home_path, file_name)
  54. if os.path.isfile(abs_file): # 上传的文件存在
  55. if self._md5sum(abs_file) == md5sum: #上传的文件md5和本地的一致,不用重新上传
  56. status_msg = {"status": "same"}
  57. self.request.send(bytes_dumps(status_msg))
  58. else: # 不一致断点续传
  59. server_file_size = os.stat(abs_file).st_size
  60. status_msg = {"status": "add", "from_size": server_file_size}
  61. self.request.send(bytes_dumps(status_msg))
  62. with open(abs_file, 'ab') as f:
  63. recv_size = server_file_size
  64. while recv_size < file_size:
  65. data = self.request.recv(4096)
  66. f.write(data)
  67. recv_size += len(data)
  68. else: #上传文件不存在,开始写入
  69. # 上传的文件大小加上家目录大小 大于 限制的家目录大小,不允许上传
  70. if getdirsize(self.home_path) + int(file_size) > int(self.limit_home.strip('M')) * 1024 * 1024:
  71. status_msg = {"status": "less"}
  72. self.request.send(bytes_dumps(status_msg))
  73. else:
  74. status_msg = {"status": "True"}
  75. self.request.send(bytes_dumps(status_msg))
  76. with open(abs_file, 'wb') as f:
  77. recv_size = 0
  78. while recv_size < file_size:
  79. data = self.request.recv(4096)
  80. f.write(data)
  81. recv_size += len(data)
  82. request_client_md5 = {"option": "md5"}
  83. self.request.send(bytes_dumps(request_client_md5))
  84. md5sum = self._md5sum(abs_file)
  85. client_md5 = str_loads(self.request.recv(1024))
  86. if client_md5['md5'] == md5sum:
  87. check_msg = {"status": "True"}
  88. else:
  89. check_msg = {"status": "False"}
  90. self.request.send(bytes_dumps(check_msg))
  91.  
  92. @staticmethod
  93. def _md5sum(filename):
  94. cmd = 'md5sum %s' % filename
  95. ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  96. md5sum = ret.stdout.read().split()[0]
  97. return str(md5sum, encoding='utf-8')
  98.  
  99. def get(self, *args, **kwargs):
  100. file_name = args[0].get('file_name')
  101. abs_filename = os.path.join(self.home_path, file_name)
  102. if os.path.isfile(abs_filename):
  103.  
  104. # 文件存在的话,向客户端发送此文件状态信息
  105. file_size = os.stat(abs_filename).st_size
  106. md5sum = self._md5sum(abs_filename)
  107. status_msg = {"status": "True", "file_size": file_size, "md5sum": md5sum}
  108. self.request.send(bytes_dumps(status_msg))
  109.  
  110. # 客户端接收状态信息后,接收客户端回复信息
  111. client_ack_msg = self.request.recv(1024)
  112. client_ack_msg = str_loads(client_ack_msg)
  113. if client_ack_msg["status"] == 'True': # 客户端没有这个文件,完整下载
  114. with open(abs_filename, 'rb') as f:
  115. while True:
  116. filedata = f.read(4096)
  117. if not filedata: break
  118. self.request.send(filedata)
  119. client_request_md5 = str_loads(self.request.recv(1024))
  120. if client_request_md5['option'] == 'md5':
  121. md5sum = self._md5sum(abs_filename)
  122. md5_msg = {"md5": md5sum}
  123. self.request.send(bytes_dumps(md5_msg))
  124. elif client_ack_msg['status'] == 'same': # 客户端和服务端文件相同
  125. pass
  126. elif client_ack_msg['status'] == 'add': # 客户端有文件但不完整
  127. from_size = int(client_ack_msg['from_size'])
  128. with open(abs_filename, 'rb') as f:
  129. f.seek(from_size)
  130. while True:
  131. filedata = f.read(4096)
  132. if not filedata: break
  133. self.request.send(filedata)
  134. else:
  135. status_msg = {"status": "False"}
  136. self.request.send(bytes_dumps(status_msg))
  137.  
  138. def cd(self, *args, **kwargs):
  139. if self.count == 0:
  140. self.tmp_home_path = self.home_path
  141.  
  142. cd_dir = args[0].get('cd_dir')
  143. if cd_dir == '/':
  144. self.home_path = self.tmp_home_path
  145. status = {"status": "True"}
  146. elif cd_dir.startswith('/'):
  147. cd_dir = cd_dir[1:]
  148. if os.path.isdir(os.path.join(self.home_path, cd_dir)):
  149. self.home_path = os.path.join(self.home_path, cd_dir)
  150. status = {"status": "True"}
  151. else:
  152. status = {"status": "False"}
  153. elif cd_dir == '':
  154. self.home_path = self.tmp_home_path
  155. status = {"status": "True"}
  156. else:
  157. if os.path.isdir(os.path.join(self.home_path, cd_dir)):
  158. self.home_path = os.path.join(self.home_path, cd_dir)
  159. status = {"status": "True"}
  160. else:
  161. status = {"status": "False"}
  162. status = bytes_dumps(status)
  163. self.request.send(status)
  164.  
  165. self.count += 1
  166.  
  167. def pwd(self, *args, **kwargs):
  168. cmd = args[0].get('action')
  169. res = subprocess.Popen(cmd, shell=True, cwd=self.home_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  170. cmd_res = res.stdout.read()
  171. cmd_res_len = len(cmd_res)
  172. status_msg = {"status": "ready", "len_ret": cmd_res_len}
  173. self.request.send(bytes_dumps(status_msg))
  174. ack_msg = self.request.recv(1024)
  175. if str_loads(ack_msg).get('status') == 'yes':
  176. self.request.send(cmd_res)
  177.  
  178. def ls(self, *args, **kwargs):
  179. cmd = args[0].get('action')
  180. args = args[0].get('args')
  181. if len(args) == 0:
  182. fnames = args = ''
  183. else:
  184. try:
  185. args, fnames = re.search('(.*-\w+ ?)(.*)', args).groups()
  186. except AttributeError as e:
  187. fnames = args
  188. args = ''
  189. cmd_res = ''
  190. if len(fnames.strip().split()) > 1:
  191. for i in fnames.strip().split():
  192. res = subprocess.Popen(cmd + ' ' + args + ' ' + self.home_path + '/' + i, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  193. cmd_res += str(res.stdout.read(), encoding='utf-8')
  194. cmd_res = bytes(cmd_res, encoding='utf-8')
  195. else:
  196. res = subprocess.Popen(cmd + ' ' + args + ' ' + self.home_path + '/' + fnames, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  197. cmd_res = res.stdout.read()
  198. if '-l' not in args:
  199. cmd_res = str(cmd_res, encoding='utf-8')
  200. tmp = []
  201. for res in cmd_res.strip().split():
  202. if os.path.isdir(os.path.join(self.home_path, res)):
  203. res = res + '/'
  204. tmp.append(res)
  205. cmd_res = ' '.join(tmp)
  206. cmd_res = bytes(cmd_res, encoding='utf-8')
  207.  
  208. cmd_res_len = len(cmd_res)
  209. if not cmd_res:
  210. cmd_res = res.stderr.read()
  211. cmd_res_len = len(cmd_res)
  212. status_msg = {"status": "ready", "len_ret": cmd_res_len}
  213. self.request.send(bytes_dumps(status_msg))
  214. ack_msg = self.request.recv(1024)
  215. if str_loads(ack_msg).get('status') == 'yes':
  216. self.request.send(cmd_res)
  217.  
  218. def _bytes_dumps(self, msg_data):
  219. return bytes(json.dumps(msg_data), encoding='utf-8')
  220.  
  221. def _str_loads(self, msg_data):
  222. return json.loads(str(msg_data, encoding='utf-8'))
  223.  
  224. def main():
  225. server = socketserver.ThreadingTCPServer(('0.0.0.0', 21), FtpServer)
  226. server.serve_forever()
  227.  
  228. def run():
  229. main()
  230.  
  231. if __name__ == '__main__':
  232. run()

5、client/ftp.py

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3.  
  4. import subprocess
  5. import socket
  6. import json
  7. import sys
  8. import os
  9.  
  10. os.sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  11. from lib.common import bytes_dumps, str_loads, bytes_encoding, str_encoding, view_bar
  12.  
  13. class FtpClient(object):
  14.  
  15. def __init__(self, fuser, fpwd, fip, fport=21):
  16. self.fuser = fuser
  17. self.fpwd = fpwd
  18. self.fip = fip
  19. self.fport = int(fport)
  20. self.s = socket.socket()
  21.  
  22. def connect_socket(self):
  23. self.s.connect((self.fip, self.fport))
  24.  
  25. def auth(self):
  26. auth_msg = {"action": "auth", "user": self.fuser, "pwd": self.fpwd}
  27. self.s.send(bytes(json.dumps(auth_msg), encoding='utf-8'))
  28. server_ack_msg = self.s.recv(1024)
  29. ack_data = json.loads(server_ack_msg.decode())
  30. if ack_data['status'] == 'True':
  31. return True
  32. else:
  33. return False
  34.  
  35. def send_data(self):
  36. while True:
  37. send_data = input('ftp> ')
  38. if len(send_data) == 0:
  39. continue
  40. cmd_list = send_data.strip().split()
  41. if cmd_list == 0:
  42. continue
  43. cmd = cmd_list[0]
  44. if hasattr(self, cmd):
  45. func = getattr(self, cmd)
  46. func(cmd_list)
  47. else:
  48. print('--暂时不支持[%s]命令' % cmd)
  49.  
  50. def put(self, *args):
  51. cmd = args[0][0]
  52. abs_filename = args[0][-1]
  53. if os.path.islink(abs_filename):
  54. print('符号链接不能上传')
  55. elif os.path.isfile(abs_filename):
  56. file_size = os.stat(abs_filename).st_size
  57. file_name = os.path.basename(abs_filename)
  58. md5sum = self._md5sum(abs_filename)
  59. msg_data = {"action": cmd, "file_name": file_name, "file_size":file_size, "md5sum": md5sum}
  60. self.s.send(bytes_dumps(msg_data))
  61. server_ack_msg = self.s.recv(1024)
  62. ack_data = str_loads(server_ack_msg)
  63. if ack_data['status'] == 'True':
  64. print('开始上传[%s]' % abs_filename)
  65. with open(abs_filename, 'rb') as f:
  66. send_size = 0
  67. while True:
  68. filedata = f.read(8192)
  69. send_size += len(filedata)
  70. if not filedata: break
  71. self.s.send(filedata)
  72. view_bar(send_size, file_size)
  73. sys.stdout.write('\n')
  74. server_request_md5 = str_loads(self.s.recv(1024))
  75. if server_request_md5['option'] == 'md5':
  76. md5sum = self._md5sum(abs_filename)
  77. md5_msg = {"md5": md5sum}
  78. self.s.send(bytes_dumps(md5_msg))
  79. md5_status = str_loads(self.s.recv(1024))
  80. if md5_status['status'] == 'True':
  81. print('发送并校验成功')
  82. else:
  83. print('上传文件失败')
  84. elif ack_data['status'] == 'add':
  85. with open(abs_filename, 'rb') as f:
  86. send_size = f.seek(ack_data['from_size'])
  87. print('开始上传[%s]' % abs_filename)
  88. while True:
  89. filedata = f.read(8192)
  90. send_size += len(filedata)
  91. if not filedata: break
  92. self.s.send(filedata)
  93. view_bar(send_size, file_size)
  94. sys.stdout.write('\n')
  95. print('增量上传成功')
  96. elif ack_data['status'] == 'same':
  97. print('开始上传[%s]' % abs_filename)
  98. print('====================================================================================================>100%')
  99. print('发送并校验成功')
  100. elif ack_data['status'] == 'less':
  101. print('磁盘空间不足')
  102. else:
  103. print('文件不存在')
  104.  
  105. @staticmethod
  106. def _md5sum(filename):
  107. cmd = 'md5sum %s' % filename
  108. ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  109. md5sum = ret.stdout.read().split()[0]
  110. return str(md5sum, encoding='utf-8')
  111.  
  112. def get(self, *args, **kwargs):
  113. cmd = args[0][0]
  114. abs_filename = args[0][-1]
  115. file_name = os.path.basename(abs_filename)
  116. msg_data = {"action": cmd, "file_name": file_name}
  117.  
  118. self.s.send(bytes_dumps(msg_data))
  119.  
  120. server_status_msg = self.s.recv(1024)
  121. status_msg = str_loads(server_status_msg)
  122.  
  123. if status_msg['status'] == 'True': # 如果要下载的文件在服务端存在
  124. file_size = status_msg['file_size']
  125.  
  126. if os.path.exists(file_name):
  127. if self._md5sum(file_name) == status_msg['md5sum']: # 本地文件和服务端相同,不用重新下载
  128. ack_msg = {"status": "same"}
  129. self.s.send(bytes_dumps(ack_msg))
  130. print('====================================================================================================>100%')
  131. print('下载并校验成功')
  132. else: # 文件存在,但校验和不同,说明没有下载完全
  133. local_file_size = os.stat(abs_filename).st_size
  134. ack_msg = {"status": "add", "from_size":local_file_size}
  135. self.s.send(bytes_dumps(ack_msg))
  136. with open(file_name, 'ab') as f:
  137. recv_size = local_file_size
  138. while recv_size < file_size:
  139. data = self.s.recv(4096)
  140. f.write(data)
  141. recv_size += len(data)
  142. view_bar(recv_size, file_size)
  143. sys.stdout.write('\n')
  144. else: # 本地没有这个文件,可以下载
  145. ack_msg = {"status": "True"}
  146. self.s.send(bytes_dumps(ack_msg))
  147. with open(file_name, 'wb') as f:
  148. recv_size = 0
  149. while recv_size < file_size:
  150. data = self.s.recv(4096)
  151. f.write(data)
  152. recv_size += len(data)
  153. view_bar(recv_size, file_size)
  154. sys.stdout.write('\n')
  155. request_server_md5 = {"option": "md5"}
  156. self.s.send(bytes_dumps(request_server_md5))
  157. md5sum = self._md5sum(file_name)
  158. server_md5 = str_loads(self.s.recv(1024))
  159. if server_md5['md5'] == md5sum:
  160. print('下载并校验成功')
  161. else:
  162. print('校验失败')
  163. else:
  164. print('服务端没有这个文件')
  165.  
  166. def pwd(self, *args, **kwargs):
  167. if len(args[0]) != 1:
  168. print('错误的参数')
  169. else:
  170. cmd = args[0][0]
  171. msg_data = {"action": cmd}
  172. self.s.send(bytes_dumps(msg_data))
  173. server_ack_msg = self.s.recv(1024)
  174. ack_data = str_loads(server_ack_msg)
  175. if ack_data['status'] == 'ready':
  176. ack_msg = {"status": "yes"}
  177. self.s.send(bytes_dumps(ack_msg))
  178. cmd_recv_size = 0
  179. cmd_ret = b''
  180. while cmd_recv_size < ack_data['len_ret']:
  181. cmd_recv = self.s.recv(1024)
  182. cmd_ret += cmd_recv
  183. cmd_recv_size += len(cmd_recv)
  184. print(str(cmd_ret, encoding='utf-8'))
  185.  
  186. def cd(self, *args, **kwargs):
  187. if len(args[0]) > 2:
  188. print('错误的参数')
  189. elif len(args[0]) == 1:
  190. cmd = args[0][0]
  191. cd_dir = ''
  192. else:
  193. cmd = args[0][0]
  194. cd_dir = args[0][1]
  195. msg_data = {"action": cmd, "cd_dir": cd_dir}
  196. self.s.send(bytes_dumps(msg_data))
  197. status = self.s.recv(1024)
  198. status = str_loads(status)
  199. if status['status'] == 'False':
  200. print('目录不存在')
  201.  
  202. def bye(self, *args, **kwargs):
  203. sys.exit(0)
  204.  
  205. def ls(self, *args):
  206. cmd = args[0][0]
  207. args = ' '.join(args[0][1:])
  208. msg_data = {"action": cmd, "args": args}
  209. self.s.send(bytes_dumps(msg_data))
  210. server_ack_msg = self.s.recv(1024)
  211. ack_data = str_loads(server_ack_msg)
  212. if ack_data['status'] == 'ready':
  213. ack_msg = {"status": "yes"}
  214. self.s.send(bytes_dumps(ack_msg))
  215. cmd_recv_size = 0
  216. cmd_ret = b''
  217. while cmd_recv_size < ack_data['len_ret']:
  218. cmd_recv = self.s.recv(1024)
  219. cmd_ret += cmd_recv
  220. cmd_recv_size += len(cmd_recv)
  221. print(str(cmd_ret, encoding='utf-8'))
  222.  
  223. def help(self, *args, **kwargs):
  224. print('--支持如下命令: ls, cd, pwd, get, put, bye')
  225.  
  226. def main():
  227.  
  228. user, ip = sys.argv[1].strip().split('@')
  229. port = sys.argv[2]
  230. pwd = input('>> 输入ftp密码:').strip()
  231.  
  232. ftp = FtpClient(user, pwd, ip, port)
  233. ftp.connect_socket()
  234. ret = ftp.auth()
  235. if ret:
  236. print('认证成功')
  237. ftp.send_data()
  238. else:
  239. print('认证失败')
  240. ftp.bye()
  241. ftp.send_data()
  242.  
  243. def run():
  244. main()
  245.  
  246. if __name__ == '__main__':
  247. run()

6、lib/common.py

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3.  
  4. import json
  5. import sys
  6. import os
  7.  
  8. def bytes_dumps( msg_data):
  9. return bytes(json.dumps(msg_data), encoding='utf-8')
  10.  
  11. def str_loads(msg_data):
  12. return json.loads(str(msg_data, encoding='utf-8'))
  13.  
  14. def bytes_encoding(msg_data):
  15. return bytes(msg_data, encoding='utf-8')
  16.  
  17. def str_encoding(msg_data):
  18. return str(msg_data, encoding='utf-8')
  19.  
  20. def view_bar(num, total):
  21. rate = num / total
  22. rate_num = int(rate * 100)
  23. r = '\r%s>%d%%' % ("="*rate_num, rate_num, )
  24. sys.stdout.write(r)
  25. sys.stdout.flush()
  26.  
  27. def getdirsize(dir):
  28. size = 0
  29. for root, dirs, files in os.walk(dir):
  30. size += sum([os.path.getsize(os.path.join(root, name)) for name in files])
  31. return size

7、lib/user_lib.py

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3.  
  4. import hashlib
  5. import re
  6. import os
  7.  
  8. BASEPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  9. USERDB = os.path.join(BASEPATH, 'db', 'user.db')
  10.  
  11. class User(object):
  12. """
  13. 实现登陆注册功能
  14. """
  15.  
  16. def __init__(self, user, password):
  17. self.username = user
  18. self.password = password
  19.  
  20. @staticmethod
  21. def check(match, filedb):
  22. """
  23. 逐行匹配,查看是否用户名已经注册,不允许重复用户名
  24. """
  25. with open(filedb, 'r') as fuser:
  26. for line in fuser:
  27. if re.match(match + '\\b', line):
  28. return False
  29. else:
  30. continue
  31. return True
  32.  
  33. @staticmethod
  34. def login_check(username, password, filedb):
  35. """
  36. 登陆验证
  37. """
  38. with open(filedb, 'r') as fuser:
  39. for line in fuser:
  40. user, pwd, _, _ = line.strip().split()
  41. if user == username and pwd == password:
  42. return True
  43. else:
  44. continue
  45. return False
  46.  
  47. def home_path(self):
  48. """
  49. 返回用户家目录
  50. """
  51. with open(USERDB, 'r') as fpath:
  52. for line in fpath:
  53. if line.startswith(self.username):
  54. return line.strip().split()[-2]
  55. else:
  56. continue
  57. return False
  58.  
  59. def limit_home(self):
  60. """
  61. 返回用户家目录限额
  62. """
  63. with open(USERDB, 'r') as flimit:
  64. for line in flimit:
  65. if line.startswith(self.username):
  66. return line.strip().split()[-1]
  67. else:
  68. continue
  69. return False
  70.  
  71. def register(self):
  72. """
  73. 注册用户
  74. """
  75. passobj = hashlib.md5(bytes(self.password, encoding='utf-8'))
  76. passobj.update(bytes(self.password, encoding='utf-8'))
  77. secure_password = passobj.hexdigest()
  78.  
  79. if self.check(self.username, USERDB):
  80. with open(USERDB, 'a') as fuser:
  81. fuser.write(self.username + ' ' + secure_password + '\n')
  82. return True
  83. else:
  84. print('用户名已存在')
  85.  
  86. def login(self):
  87. """
  88. 用户登陆
  89. """
  90. passobj = hashlib.md5(bytes(self.password, encoding='utf-8'))
  91. passobj.update(bytes(self.password, encoding='utf-8'))
  92. secure_password = passobj.hexdigest()
  93. ret = self.login_check(self.username, secure_password, USERDB)
  94. if ret:
  95. return True
  96. else:
  97. return False
  98.  
  99. def modify(self):
  100. """
  101. 修改密码
  102. """
  103. pass

8、db/user.db

  1. hexm 92df47e9074c048e0afe84ce0a5c407d /tmp 1M
  2. xm 92df47e9074c048e0afe84ce0a5c407d /tmp/sb 5M

socketserver模块写的一个简单ftp程序的更多相关文章

  1. 用python开发简单ftp程序

    根据alex老师视频开发的简单ftp程序,只能实现简单的get功能 ftp客户端程序: #!/usr/bin/env python #_*_ coding:utf-8 _*_ import socke ...

  2. 写了一个简单的CGI Server

    之前看过一些开源程序的源码,也略微知道些Apache的CGI处理程序架构,于是用了一周时间,用C写了一个简单的CGI Server,代码算上头文件,一共1200行左右,难度中等偏上,小伙伴可以仔细看看 ...

  3. 只是一个用EF写的一个简单的分页方法而已

    只是一个用EF写的一个简单的分页方法而已 慢慢的写吧.比如,第一步,先把所有数据查询出来吧. //第一步. public IQueryable<UserInfo> LoadPagesFor ...

  4. 关于SIGSLOT的一个简单的程序

    废话少说直接看代码即可,这只是一个简单的程序,可以帮我们简单地明白SIGSLOT是怎么回事.至于深入研究自己去百度吧. #include "sigslot.h" using nam ...

  5. 用Racket语言写了一个万花筒的程序

    用Racket语言写了一个万花筒的程序 来源:https://blog.csdn.net/chinazhangyong/article/details/79362394 https://github. ...

  6. 自己写的一个简单PHP采集器

    自己写的一个简单PHP采集器 <?php //**************************************************************** $url = &q ...

  7. 写了一个简单可用的IOC

    根据<架构探险从零开始写javaweb框架>内容写的一个简单的 IOC 学习记录    只说明了主要的类,从上到下执行的流程,需要分清主次,无法每个类都说明,只是把整个主线流程说清楚,避免 ...

  8. 写了一个简单的 Mybatis

    写了一个简单的 Mybatis,取名 SimpleMybatis . 具备增删改查的基本功能,后续还要添加剩下的基本数据类型和Java集合类型的处理. 脑图中有完整的源码和测试的地址 http://n ...

  9. 扩展Python模块系列(二)----一个简单的例子

    本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...

随机推荐

  1. Android开发之Fragment

    一.Fragment生命周期: 二.动态添加Fragment的三步: 1.获得Fragment的管理者FragmentManager FragmentManager fragmentManager = ...

  2. alarm

    AlarmManager的使用机制有的称呼为全局定时器,有的称呼为闹钟.通过对它的使用,它的作用和Timer有点相似.都有两种相似的用法:(1)在指定时长后执行某项操作 (2)周期性的执行某项操作 在 ...

  3. 常用数据库高可用和分区解决方案(2) — MongoDB篇

    MongoDB是当前比较流行的文档型数据库,其拥有易使用.易扩展.功能丰富.性能卓越等特性.MongoDB本身就拥有高可用及分区的解决方案,分别为副本集(Replica Set)和分片(shardin ...

  4. hibernate用setResultTransformer转换

    当你用hibernate查出数据,但是类型不是原来的类型怎么办,新增的实体类还可以用,query.setResultTransformer(Transformers.aliasToBean(AA.cl ...

  5. 网络设计中需要考虑的时延latency差异

    Jeff Dean提到不同数据访问方式latency差异 Numbers Everyone Should Know L1 cache reference 0.5 ns Branch mispredic ...

  6. 开发错误日记 12: Unsupported major.minor version 52.0

    开发错误日记 12: Unsupported major.minor version 52.0 在编译时出现如下错误: java.lang.UnsupportedClassVersionError: ...

  7. java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 错误

    你的ArrayList 是一个没有值的对象(不是null),也就是里面什么对象也没有存(即:arrayList.size()==0).但是,你有取它下标为0值的操作.所以,数组越界了!!比如array ...

  8. 512M内存编译php出错

    make时错误如下 virtual memory exhausted: Cannot allocate memory make: *** [ext/fileinfo/libmagic/apprenti ...

  9. sublime text 如何设置”在浏览器浏览“的快捷键

    sublime text编辑器极其强大 ,但在刚开始用的时候,每次在浏览器中查看非得右键鼠标----”open in browser“,着实觉得麻烦....百度之,上面的方法有很多,但是根据我自己的经 ...

  10. js json 对象相互转换

    字符串转对象(strJSON代表json字符串)   var obj = eval(strJSON);   var obj = strJSON.parseJSON();   var obj = JSO ...