目录:

  一、基础概念

    1、动态导入模块

    2、断言机制

    3、SocketServer

  二、作业 FTP服务器

  三、单独注明

    1、类中装饰器

    2、进度条

一、基础概念

  1、动态导入模块

  有时在编程过程中,如果知道模块对应的字符串,可以通过动态导入的方式将该字符对应的模块进行动态导入

导入的方式有两种:

  1、python解释器默认的方式导入

  2、官方推荐动态导入方式

下面来分别介绍以下两种方式:

lib模块中包括aa文件,aa文件内容如下:

class C(object):

    def __init__(self):
self.name = 'gavin'

通过方法一导入:

modname = 'lib'  #知道模块名字对应字符串

mod = __import__('lib.aa') #导入lib同时导入lib模块下的aa,但是这个mod只是lib,而不是lib.aa  要记住

obj = mod.aa.C()#因为mod只表示lib,所以需要写成mod.aa表示lib.aa
print(obj.name)

通过方法二导入:

import importlib
mod = importlib.import_module('lib.aa') #官方建议这种方法更有效,mod直接表示了lib.aa obj = mod.C()
print(obj.name)

  2、断言机制

断言的作用:

当后续的程序很重要,不允许出错,可以在该程序前添加断言机制,当断言不正确就不执行,断言正确会继续执行后续的程序

例子:

借用上面的lib模块引用,当引用该模块后,发现其中的类型为str类型后,打印,然后将类型断言变为int类型,此时会发现原来正常打印执行的语句不再执行

  1、正确打印:

import importlib

mod_name = 'lib.aa'
mod = importlib.import_module(mod_name) obj = mod.C()
assert type(obj.name) is str
print('断言正确执行')
#输出
断言正确执行

  2、忽略打印动作

import importlib

mod_name = 'lib.aa'
mod = importlib.import_module(mod_name) obj = mod.C()
assert type(obj.name) is int
print('断言正确执行')
#输出
raceback (most recent call last):
File "/Users/Gavin/PycharmProjects/python/day8/复现/2-断言/断言.py", line 7, in <module>
assert type(obj.name) is int
AssertionError

   3、socketserver

socketserver是一个模块,该模块简化了编写网络服务器的任务复杂度

socketserver一共有这么几种类型

  1、tcp类型

class socketserver.TCPServer(server_address, RequestHandlerClass,bind_and_activate=True)

  2、udp类型

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

  3、使用Unix domain方式socket类型

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)
这几种类的继承关系如下:
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+

创建一个socketserver至少分以下几个步骤:

  1、必须创建一个请求处理类,这个要继承BaseRequestHandler类并且要重写父类里的handle方法

  2、必须要实例化一个其中任意类型的类,同时传递Server IP和上面创建的请求处理类给这个任意类

  3、调用类方法

  类方法包含以下两种:

    1、server.serve_request() #只处理一个请求,不会使用这个

    2、server.serve_forever()#处理多个请求,永远执行着调用

  4、关闭server.serve_forever()

  注意:跟客户端所有交互,都是在handle()方法中完成的,每个请求过来以后,都是通过handle来规定处理行为

基本的sockerserver代码

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server. It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
""" def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper()) if __name__ == "__main__":
HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()

在上面代码,仍然不能同时处理多个链接,如果要处理多个链接,需要将socketserver.TCPServer变为以下类:

class socketserver.ForkingTCPServer

class socketserver.ForkingUDPServer

class socketserver.ThreadingTCPServer

class socketserver.ThreadingUDPServer

所以只需要替换

server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)

变为下面这个,就可以多并发了

server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)

class socketserver.BaseServer(server_addressRequestHandlerClass) 主要有以下方法

class socketserver.BaseServer(server_address, RequestHandlerClass)
This is the superclass of all Server objects in the module. It defines the interface, given below, but does not implement most of the methods, which is done in subclasses. The two parameters are stored in the respective server_address and RequestHandlerClass attributes. fileno()
Return an integer file descriptor for the socket on which the server is listening. This function is most commonly passed to selectors, to allow monitoring multiple servers in the same process. handle_request()
Process a single request. This function calls the following methods in order: get_request(), verify_request(), and process_request(). If the user-provided handle() method of the handler class raises an exception, the server’s handle_error() method will be called. If no request is received within timeout seconds, handle_timeout() will be called and handle_request() will return. serve_forever(poll_interval=0.5)
Handle requests until an explicit shutdown() request. Poll for shutdown every poll_interval seconds. Ignores the timeout attribute. It also calls service_actions(), which may be used by a subclass or mixin to provide actions specific to a given service. For example, the ForkingMixIn class uses service_actions() to clean up zombie child processes. Changed in version 3.3: Added service_actions call to the serve_forever method. service_actions()
This is called in the serve_forever() loop. This method can be overridden by subclasses or mixin classes to perform actions specific to a given service, such as cleanup actions. New in version 3.3. shutdown()
Tell the serve_forever() loop to stop and wait until it does. server_close()
Clean up the server. May be overridden. address_family
The family of protocols to which the server’s socket belongs. Common examples are socket.AF_INET and socket.AF_UNIX. RequestHandlerClass
The user-provided request handler class; an instance of this class is created for each request. server_address
The address on which the server is listening. The format of addresses varies depending on the protocol family; see the documentation for the socket module for details. For Internet protocols, this is a tuple containing a string giving the address, and an integer port number: ('127.0.0.1', 80), for example. socket
The socket object on which the server will listen for incoming requests. The server classes support the following class variables: allow_reuse_address
Whether the server will allow the reuse of an address. This defaults to False, and can be set in subclasses to change the policy. request_queue_size
The size of the request queue. If it takes a long time to process a single request, any requests that arrive while the server is busy are placed into a queue, up to request_queue_size requests. Once the queue is full, further requests from clients will get a “Connection denied” error. The default value is usually 5, but this can be overridden by subclasses. socket_type
The type of socket used by the server; socket.SOCK_STREAM and socket.SOCK_DGRAM are two common values. timeout
Timeout duration, measured in seconds, or None if no timeout is desired. If handle_request() receives no incoming requests within the timeout period, the handle_timeout() method is called. There are various server methods that can be overridden by subclasses of base server classes like TCPServer; these methods aren’t useful to external users of the server object. finish_request()
Actually processes the request by instantiating RequestHandlerClass and calling its handle() method. get_request()
Must accept a request from the socket, and return a 2-tuple containing the new socket object to be used to communicate with the client, and the client’s address. handle_error(request, client_address)
This function is called if the handle() method of a RequestHandlerClass instance raises an exception. The default action is to print the traceback to standard output and continue handling further requests. handle_timeout()
This function is called when the timeout attribute has been set to a value other than None and the timeout period has passed with no requests being received. The default action for forking servers is to collect the status of any child processes that have exited, while in threading servers this method does nothing. process_request(request, client_address)
Calls finish_request() to create an instance of the RequestHandlerClass. If desired, this function can create a new process or thread to handle the request; the ForkingMixIn and ThreadingMixIn classes do this. server_activate()
Called by the server’s constructor to activate the server. The default behavior for a TCP server just invokes listen() on the server’s socket. May be overridden. server_bind()
Called by the server’s constructor to bind the socket to the desired address. May be overridden. verify_request(request, client_address)
Must return a Boolean value; if the value is True, the request will be processed, and if it’s False, the request will be denied. This function can be overridden to implement access controls for a server. The default implementation always returns True.

二、作业

  开发一个支持多用户在线的FTP程序

  要求:

    1.   用户加密认证
    2.   允许同时多用户登录
    3.   每个用户有自己的家目录 ,且只能访问自己的家目录
    4.   对用户进行磁盘配额,每个用户的可用空间不同
    5.   允许用户在ftp server上随意切换目录(切换目录命令参数只包括 '.'   '..' 和绝对路径)
    6.   允许用户查看当前目录下文件
    7.   允许上传和下载文件,保证文件一致性
    8.   文件传输过程中显示进度条
    9. 附加功能:支持文件的断点续传(没实现,可以通过抓取异常处理,然后通过tell记录字节数,下一次传递通过seek到记录的字节数开始继续传输来实现

  


目录结构:

代码实现:

FTP_Client:

import os
import sys
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(BASEDIR)
sys.path.insert(0,BASEDIR) from core import main if __name__ == '__main__':
#HOST = input('输入你链接的地址:').strip()
#PORT = int(input('输入你要链接的端口:').strip())
obj = main.FtpClient('localhost',9999)
obj.FtpManager()

ftp_client.py

import os
import sys
import logging
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,BASEDIR) USER_HOME = os.path.join(BASEDIR,'user_data/')

settings

import socket
import os
import sys
import hashlib
import json
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,BASEDIR) from config import settings class FtpClient(object): def __init__(self,host,port): #构造函数,初始化时可以指定服务器IP与端口
self.host = host
self.port = port
self.client = socket.socket() def help(self): #认证成功后,显示该客户端功能菜单
msg = {
'cd': 'cd .|cd .. |cd abspath 三种方式切换路径',
'ls': '显示目录内容',
'pwd': '显示当前路径',
'put filename': '上传文件',
'get filename': '下载文件',
'quit': '退出程序',
}
for line in msg:
print(line, msg[line]) def FtpManager(self):
self.client.connect((self.host,self.port))
while True:
username = input('\033[32;1m输入用户名:\033[0m').strip()
if username != 'q':
password = input('\033[32;1m输入密码:\033[0m').strip().encode('utf-8')
password_md5 = hashlib.md5()
password_md5.update(password) #对密码进行加密
user_dic = {
"username": username,
"password": password_md5.hexdigest()
} self.client.send(json.dumps(user_dic).encode('utf-8'))
server_auth_ack = int(self.client.recv(1024).decode())
if server_auth_ack == 200: #认证成功后进入菜单
while True:
getattr(self,'help')()
cmd_list= ['ls', 'pwd', 'cd']
cmd = input('#').strip()
if len(cmd) == 0:
continue
elif cmd.split()[0] in cmd_list:
func = getattr(self,'interactive')
break_flag = func(cmd)
if break_flag == 202:
break
elif hasattr(self,cmd.split()[0]):
func = getattr(self,cmd.split()[0])
break_flag = func(cmd)
if break_flag == 202:
break else:
print('\033[31;1m没有这个选项,请重新输入\033[0m') elif server_auth_ack == 400:
print('\033[31;1m用户名错误,请重新输入\033[0m')
elif server_auth_ack == 401:
print('\033[31;1m密码错误,请重新输入\033[0m')
else:
exit('\033[31;1m退出程序\033[0m') def quit(self,*args):
while True:
cmd_split = args[0].split()
msg_dic = {
'action': 'quit'
}
self.client.send(json.dumps(msg_dic).encode('utf-8'))
quit_ack = self.client.recv(1024).decode()
if quit_ack == '':
exit('\033[31;1m退出程序\033[0m')
else:
continue def put(self,*args):
cmd_split = args[0].split()
if len(cmd_split) > 1:
filename = cmd_split[1]
File_Name = settings.USER_HOME + filename
if os.path.isfile(File_Name):
File_size = os.stat(File_Name).st_size
msg_dic = {
'action': 'put',
'filename': filename,
'filesize': File_size,
'overridden': True
}
self.client.send(json.dumps(msg_dic).encode('utf-8'))
server_response = int(self.client.recv(1024).decode())
if server_response == 200:
with open(File_Name, 'rb') as f: for line in f:
self.client.send(line)
put_done_ack = self.client.recv(1024).decode()
print('\033[31;1m%s :传输完毕\033[0m' % put_done_ack)
elif server_response == 500:
print('\033[31;1m该用户没有上传权限\033[0m')
elif server_response == 300:
print('\033[31;1m该文件已存在\033[0m')
elif server_response == 501:
print('\033[31;1m磁盘配额已用完\033[0m')
elif server_response == 202:
print('\033[31;1m没有登录,请重新登录\033[0m')
return 202
else:
print('\033[31;1m该文件不存在\033[0m')
else:
print('\033[31;1m命令错误请重新输入\033[0m') def get(self,*args):
cmd_split = args[0].split()
if len(cmd_split) > 1:
filename = cmd_split[1]
File_Name = settings.USER_HOME + filename
if os.path.isfile(File_Name):
print('\033[31;1m该文件已存在\033[0m')
else:
msg_dic = {
'action': 'get',
'filename': filename
}
self.client.send(json.dumps(msg_dic).encode('utf-8'))
server_response = int(self.client.recv(1024).decode())
if server_response == 200:
self.client.send(''.encode('utf-8'))
msg_recv = json.loads(self.client.recv(1024).decode())
self.client.send(''.encode('utf-8'))
filesize = msg_recv['filesize']
recv_size = 0
with open(File_Name, 'wb') as f:
while recv_size < filesize:
if filesize - recv_size > 1024:
size = 1024
else:
size = filesize - recv_size data = self.client.recv(size)
f.write(data)
f.flush()
recv_size += len(data)
pri_size = int(recv_size/filesize*100)
pri_fun = pri_size +1
str = '>'*int(pri_size/2) + ' '*int((100-pri_fun)/2)
sys.stdout.write('\r' + str + '%s%%' %pri_size)
sys.stdout.flush() self.client.send(''.encode('utf-8'))
print('')
print('\033[31;1m传输完毕\033[0m')
elif server_response == 300:
print('\033[31;1m文件在服务器上不存在\033[0m')
elif server_response == 202:
print('\033[31;1m没有登录,请重新登录\033[0m')
return 202
else:
print('\033[31;1m命令错误请重新输入\033[0m') def interactive(self,*args):
cmd_split = args[0].split()
if len(cmd_split) >1:
if len(cmd_split) < 3:
msg_dic = {
'action': cmd_split[0],
'dict': cmd_split[1]
}
self.client.send(json.dumps(msg_dic).encode('utf-8'))
ack_flag = int(self.client.recv(1024).decode())
if ack_flag == 200:
self.client.send(''.encode('utf-8'))
pwd_res = self.client.recv(1024).decode()
print(pwd_res)
elif ack_flag == 202:
print('\033[31;1m用户没有登录\033[0m')
return 202
else:
print('\033[31;1m命令错误请重新输入\033[0m') elif len(cmd_split) == 1:
msg_dic = {
'action': cmd_split[0]
}
self.client.send(json.dumps(msg_dic).encode('utf-8'))
ack_flag = int(self.client.recv(1024).decode())
if ack_flag == 200:
self.client.send(''.encode('utf-8'))
pwd_res = json.loads(self.client.recv(1024).decode())
if isinstance(pwd_res, list):
for line in pwd_res:
print(line)
elif isinstance(pwd_res,str):
print(pwd_res)
elif ack_flag == 202:
print('\033[31;1m用户没有登录\033[0m')
return 202

main.py

FTP_Server:

import os
import sys
import socketserver
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,BASEDIR) from core import main if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = socketserver.ThreadingTCPServer((HOST, PORT), main.MyTCPHandler)
server.serve_forever()

ftp_server.py

import os
import sys
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,BASEDIR) DATABASE_USER = {
'path': '%s/db' %BASEDIR
} USER_HOME = {
'path': '%s/home' %BASEDIR
} USER_TEMP = {
'path': '%s/temp' %BASEDIR
}

settings.py

import os
import sys
import socketserver
import json
import hashlib
import re
import time
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0,BASEDIR) from config import settings class MyTCPHandler(socketserver.BaseRequestHandler): def auth(self,*args):
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[0]['username']) #导入用户存在目录
if os.path.isfile(user_auth): #判断该目录是否存在
user_passwd = hashlib.md5()
with open(user_auth,'r') as f:
user_dic = json.load(f)
user_passwd.update(user_dic['password'].encode('utf-8')) #hashlib加密需要提前做encode
if user_passwd.hexdigest() == args[0]['password']:
user_dic['authenticated'] = 1
with open(user_auth,'w') as f:
json.dump(user_dic,f)
return 200
else:
return 401
else:
return 400 def handle(self): #socketserver模块中处理请求的方法
while True:
try:
user_data = json.loads(self.request.recv(1024).decode()) #接受到用户发送过来的json用户名与密码
auth_code = getattr(self,'auth')(user_data) #通过auth方法处理
self.request.send(str(auth_code).encode('utf-8'))
if auth_code != 200:continue #验证不成功重新验
while True:
data = self.request.recv(1024) #当验证成功后接收客户端指令
cmd_dic = json.loads(data.decode())
action = cmd_dic['action'] #提取客户端发送过来的指令 if hasattr(self,action):
func = getattr(self,action) #根据指令反射对应方法
func(cmd_dic,user_data)
except ConnectionResetError as e:
print("err",e)
break def check_auth(func): #装饰器,用于验证用户是否登录,
def deco(self,*args):
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
with open(user_auth, 'r') as f:
user_dic = json.load(f)
if user_dic['authenticated'] == 1:#在装饰器中,如果用户已登录会在登录函数auth中将authenticated置为1
func(self,*args)
else:
print('not authed')
self.request.send(''.encode('utf-8'))
return deco @check_auth
def put(self,*args):
'''接受客户端文件'''
cmd_dic = args[0]
filename = cmd_dic['filename']
filesize = cmd_dic['filesize']
user_dic = args[1]
user_db = self.user_home(user_dic) #在操作前判断用户当前所在目录
#BASE_HOME = '%s/%s/' %(settings.USER_HOME['path'], args[1]['username']) #确定客户端对应服务器所在目录
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username']) #确定用户认证文件,里面包括用户名与磁盘配额信息
for key in user_db:
user_db_value = user_db[key]
with open(user_auth,'r') as f:
user_dic = json.load(f)
if user_dic['allow_update'] == True: #判断用户是否允许上传文件
quato = user_dic['free_disk']
if filesize < quato: #需要查看用户磁盘配额
if os.path.isfile(user_db_value + filename): #判断文件是否存在
self.request.send(''.encode('utf-8'))
else:
self.request.send(''.encode('utf-8'))
received_size = 0
with open(user_db_value + filename, 'wb') as f:
'''根据文件大小准确写入文件内容'''
while received_size < filesize:
if filesize - received_size > 1024:
size = 1024
else:
size = filesize - received_size data = self.request.recv(size)
f.write(data)
f.flush()
received_size += len(data)
pri_size = int(received_size/filesize*100)
pri_fun = pri_size +1
str = '>'*int(pri_size/2) + ' '*int((100-pri_fun)/2)
send_result = str + '%s%%' %pri_size
sys.stdout.write('\r' + str + '%s%%' %pri_size)
sys.stdout.flush()
self.request.send(''.encode('utf-8'))
user_dic['free_disk'] = quato - filesize #磁盘配额计算
with open(user_auth,'w') as f: #新磁盘配额重新写入
json.dump(user_dic,f)
else:
self.request.send(''.encode('utf-8'))
else:
self.request.send(''.encode('utf-8')) @check_auth
def get(self,*args):
'''发生文件给客户端'''
cmd_dic = args[0]
filename = cmd_dic['filename']
user_dic = args[1]
user_db = self.user_home(user_dic) #判断当前所在目录
#BASE_HOME = '%s/%s/' %(settings.USER_HOME['path'], args[1]['username'])
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
for key in user_db:
user_db_value = user_db[key]
if not os.path.isfile(user_db_value + '/'+ filename): #如果文件不存在,返回参数
self.request.send(''.encode('utf-8'))
else:
self.request.send(''.encode('utf-8'))
self.request.recv(1024)
filesize = int(os.stat(user_db_value + '/' + filename).st_size)
msg_dic = {
'filename': filename,
'filesize': filesize
}
self.request.send(json.dumps(msg_dic).encode('utf-8')) #发送文件和大小
self.request.recv(1024)
with open(BASE_HOME + filename,'rb') as f:
for line in f:
self.request.send(line)
get_done_ack = self.request.recv(1024)
print('\033[31;1m%s: 传输完毕\033[0m' % get_done_ack) def user_home(self,*args): #判断当前所在目录函数
'''获取用户base_home信息'''
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[0]['username'])
user_temp = '%s/%s.json' %(settings.USER_TEMP['path'], args[0]['username'])
if os.path.isfile(user_temp): #当user_temp文件存在时,会先读取该文件所在内容
with open(user_temp,'r') as f:
temp_dic = json.load(f)
else:
with open(user_auth,'r') as f:
user = json.load(f)
temp_dic = user['Base_Home']
return temp_dic def quit(self,*args):
'''在程序退出时,会将装饰器中查看字段修改回0,同时删除用户跳转目录的记录文件'''
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
with open(user_auth,'r') as f:
user_dic = json.load(f)
user_dic['authenticated'] = 0 #装饰器置为0
with open(user_auth,'w') as f:
json.dump(user_dic,f)
self.request.send(''.encode('utf-8'))
user_temp = '%s/%s.json' %(settings.USER_TEMP['path'], args[1]['username'])
if os.path.isfile(user_temp):
os.remove(user_temp) #删除用户操作记录文件 @check_auth
def pwd(self,*args):
'''查看当前目录'''
cmd_dic = args[0]
user_dic = args[1]
user_db = self.user_home(user_dic) #先查看当前目录在哪个位置
print(user_db)
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
user_temp = '%s/%s.json' %(settings.USER_TEMP['path'], args[1]['username'])
self.request.send(''.encode('utf-8'))
client_ack = int(self.request.recv(1024).decode())
if client_ack == 200:
for key in user_db:
user_db_key = key
user_db_key = json.dumps(user_db_key)
self.request.send(user_db_key.encode('utf-8'))
else:
self.request.send(''.encode('utf-8')) @check_auth
def ls(self,*args):
'''查看当前目录'''
cmd_dic = args[0]
user_dic = args[1]
user_db = self.user_home(user_dic)
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
user_temp = '%s/%s.json' %(settings.USER_TEMP['path'], args[1]['username'])
self.request.send(''.encode('utf-8'))
client_ack = int(self.request.recv(1024).decode())
if client_ack == 200:
for key in user_db:
user_db_value = user_db[key]
res = os.listdir(user_db_value)
res = json.dumps(res)
self.request.send(res.encode('utf-8'))
else:
self.request.send(''.encode('utf-8')) @check_auth
def cd(self,*args):
cmd_dic = args[0]
user_dic = args[1]
user_db = self.user_home(user_dic)
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
user_temp = '%s/%s.json' %(settings.USER_TEMP['path'], args[1]['username'])
self.request.send(''.encode('utf-8'))
client_ack = int(self.request.recv(1024).decode())
if client_ack == 200:
with open(user_auth,'r') as f:
user = json.load(f)
user_home = user['Base_Home']
if user_db == user_home: #当前目录为用户家目录时操作
for key in user_db:
user_db_key = key
user_db_value = user_db[key]
if cmd_dic['dict'] == '.' or cmd_dic['dict'] == '..': #当前目录为家目录,.和..均返回家目录
self.request.send(user_db_key.encode('utf-8'))
else:
r_match = '^' + '(' + user_db_key + ')' + '{1}' + '(.*)' #定义re,内容必须包括家目录字段,不然认为目录不存在
user_dict = re.search(r_match , cmd_dic['dict'])
if user_dict.groups() is not None:#当条件匹配时通过拼接方式更新服务器真正目录
user_db_value_new = user_db_value + user_dict.groups()[1] #
if os.path.isdir(user_db_value_new):#判断该目录是否存在
os.chdir(user_db_value_new) #如果存在,更改目录
self.request.send(cmd_dic['dict'].encode('utf-8'))
del user_db[user_db_key] #删除用户目录对应的字典
user_db[cmd_dic['dict']] = user_db_value_new #重新填写更新内容
with open(user_temp, 'w') as f:
json.dump(user_db,f) #将用户操作字典写入文件
else:
self.request.send('dictory is not exist!'.encode('utf-8'))
else:
'''当前目录不是用户家目录时操作'''
for key in user_db:
user_db_key = key
user_db_value = user_db[key]
with open(user_auth,'r') as f:
user = json.load(f)
user_home = user['Base_Home']
for key in user_home:
user_home_value = user_home[key]
user_home_key = key
if cmd_dic['dict'] == '.':
self.request.send(user_db_key.encode('utf-8'))
elif cmd_dic['dict'] == '..':
user_db_value_new = os.path.split(user_db_value)[0]
user_db_key_new = os.path.split(user_db_key)[0]
r_match = '^' + '(' + user_home_key + ')' + '{1}' + '(.*)' #re对比的源目录为用户家目录
user_dict = re.search(r_match , user_db_value_new)
if user_dict.groups() is not None:
user_db_value_new = user_home_value + user_dict.groups()[1]
if os.path.isdir(user_db_value_new):
os.chdir(user_db_value_new)
self.request.send(user_db_key_new.encode('utf-8'))
del user_db[user_db_key]
user_db[user_db_key_new] = user_db_value_new
with open(user_temp, 'w') as f:
json.dump(user_db,f)
else:
self.request.send('已超过根目录'.encode('utf-8'))
else: r_match = '^' + '(' + user_home_key + ')' + '{1}' + '(.*)'
user_dict = re.search(r_match , cmd_dic['dict'])
if user_dict.groups() is not None:
user_db_value_new = user_home_value + user_dict.groups()[1] if os.path.isdir(user_db_value_new):
os.chdir(user_db_value_new)
self.request.send(cmd_dic['dict'].encode('utf-8'))
del user_db[user_db_key]
user_db[cmd_dic['dict']] = user_db_value_new
with open(user_temp, 'w') as f:
json.dump(user_db,f)
else:
self.request.send('dictory is not exist!'.encode('utf-8'))

mainpy

{"name": "chenronghua", "allow_update": true, "password": "", "authenticated": 1, "Base_Home": {"/home/chenronghua": "/Users/Gavin/PycharmProjects/python/day8/Ftp/Ftp-server/home/chenronghua"}, "free_disk": 9267344720}

XXX.json

  三、单独标注:

    1、类中装饰器

 def check_auth(func):  #装饰器,用于验证用户是否登录,
def deco(self,*args):
user_auth = '%s/%s.json' %(settings.DATABASE_USER['path'], args[1]['username'])
with open(user_auth, 'r') as f:
user_dic = json.load(f)
if user_dic['authenticated'] == 1:#在装饰器中,如果用户已登录会在登录函数auth中将authenticated置为1
func(self,*args)
else:
print('not authed')
self.request.send(''.encode('utf-8'))
return deco

    2、进度条

recv_size = 0
with open(File_Name, 'wb') as f:
while recv_size < filesize:
if filesize - recv_size > 1024:
size = 1024
else:
size = filesize - recv_size data = self.client.recv(size)
f.write(data)
f.flush()
recv_size += len(data)
pri_size = int(recv_size/filesize*100)
pri_fun = pri_size +1
str = '>'*int(pri_size/2) + ' '*int((100-pri_fun)/2)
sys.stdout.write('\r' + str + '%s%%' %pri_size)
sys.stdout.flush()

Python学习笔记第八周的更多相关文章

  1. Python学习笔记(八)

    Python学习笔记(八): 复习回顾 递归函数 内置函数 1. 复习回顾 1. 深浅拷贝 2. 集合 应用: 去重 关系操作:交集,并集,差集,对称差集 操作: 定义 s1 = set('alvin ...

  2. Python学习笔记第五周

    目录 一.基础概念 1.模块定义 2.包的定义 3.导入包的本质 4.导入模块的本质 5.导入方法 6.import的本质 7.导入优化 8.模块分类 9.标准库介绍 1.time与datetime ...

  3. Python学习二十八周(vue.js)

    一.指令 1.一个例子简单实用vue: 下载vue.js(这里实用1.0.21版本) 编写html代码: <!DOCTYPE html> <html lang="en&qu ...

  4. Python学习笔记第二十三周(Flask架构)

    目录: 一.变量引用 内容: 备注:PyCharm小技巧,comm+alt+l  自动修改格式,comm+alt+return  向上添加新行 一.变量引用 1.url生成 from flask im ...

  5. Python学习笔记(八)sorted

    摘抄自:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431823058 ...

  6. python 学习笔记十八 django深入学习三 分页,自定义标签,权限机制

    django  Pagination(分页) django 自带的分页功能非常强大,我们来看一个简单的练习示例: #导入Paginator>>> from django.core.p ...

  7. python学习笔记-(八)装饰器、生成器&迭代器

    本节课程内容概览: 1.装饰器 2.列表生成式&迭代器&生成器 3.json&pickle数据序列化 1. 装饰器 1.1 定义: 本质上是个函数,功能是装饰其他函数—就是为其 ...

  8. Python学习笔记第二十七周(Bootstrap)

    目录: 全局样式 一.栅格系统 二.表单 三.按钮  四.导航 五.按钮组 六.面板 七.表格 八.分页 九.排版 十.图片 十一.辅助类 十二.响应式工具 组件 内容: 前言: 首先通过https: ...

  9. Python学习笔记第十一周

    目录: 1.RabbitMQ   2.Redis  内容: 1.RabbitMQ 实现简单的队列通信 send端 import pika credentials = pika.PlainCredent ...

随机推荐

  1. ThinkPHP3上传文件中遇到的问题

    在用ThinkPHP3上传图片的开发过程中遇到如下几个问题: 上传根目录不存在!请尝试手动创建:./Uploads/: 上传的图片,中文名称乱码: 上传文件时会自动生成一个以当前日期命名的文件夹,并将 ...

  2. [hdu 6191] Query on A Tree

    Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Othe ...

  3. 音频 API 一览

    iOS 和 OS X 平台都有一系列操作音频的 API,其中涵盖了从低到高的全部层级.随着时间的推移.平台的增长以及改变,不同 API 的数量可以说有着非常巨大的变化.本文对当前可以使用的 API 以 ...

  4. 常用的flex知识 ,比起float position 好用不少

      flex布局具有便捷.灵活的特点,熟练的运用flex布局能解决大部分布局问题,这里对一些常用布局场景做一些总结. web页面布局(topbar + main + footbar) 示例代码   要 ...

  5. grep console

    FF6464 9F6B00 8A8A00 4B5E76 8A7674 980B4F 980B4F

  6. Java Date实现加一天,年月日类推往后+1,日期+1,月份+1,年份+1

    System.out.println("String类型 "+endDate); //页面传递到后台的时间 为String类型 SimpleDateFormat sdf = new ...

  7. UVa LA 3213 - Ancient Cipher 水题 难度: 0

    题目 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_pr ...

  8. flask项目结构(六)快速开发后台flask-admin

    简介: Flask-admin 相当django的xadmin吧! 快速装配一个后台用来管理数据. Flask-admin也是有使用局限性的,他只适合开发小型快速的应用,不适合那种大型并发性高,逻辑复 ...

  9. 《Python》网络编程之客户端/服务端框架、套接字(socket)初使用

    一.软件开发的机构 我们了解的涉及到两个程序之间通讯的应用大致可以分为两种: 第一种是应用类:QQ.微信.网盘等这一类是属于需要安装的桌面应用 第二种是web类:比如百度.知乎.博客园等使用浏览器访问 ...

  10. 《Python》模块和包

    一.模块 1.什么是模块: 一个模块就是一个包含了Python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1.使用Python编写的代码(. ...