FTP上传下载文件
实现:服务器与客户端的登陆,注册,上传文件,下载文件,退出
server.py
import socketserver
import json
import hashlib
import os
import struct
# 获取当前路径,方便项目移植时,路径正确
base_path = os.path.dirname(__file__)
# 获取当前账户文件路径
userinfo = os.path.join(base_path,"db","userinfo.txt")
class Auth:
@staticmethod
def md5(usr,pwd):
md5_obj = hashlib.md5(usr.encode())
md5_obj.update(pwd.encode())
return md5_obj.hexdigest()
@classmethod
def register(cls,opt_dic):
# 1.先检测账户文件是否已经有该账户
with open(userinfo,mode="r",encoding="utf-8") as fp:
for line in fp:
username = line.split(":")[0]
if username == opt_dic['user']:
return {'result':False,"info":"用户名存在了"}
# 2.当前用户可以注册
with open(userinfo,mode="a",encoding="utf-8") as fp:
fp.write("%s:%s\n" % (opt_dic['user'],cls.md5(opt_dic['user'],opt_dic['passwd'])))
# 将来用户上传文件时候,为当前用户创建上传文件夹
# 未完待续...
return {'result':True,"info":"注册成功"}
@classmethod
def login(cls,opt_dic):
with open(userinfo,mode="r",encoding="utf-8") as fp:
"""
for... else python特有
当循环遇到break,或者return 意外终止时,不执行else分支,其他情况都执行else
"""
for line in fp:
username,password = line.strip().split(":")
if username == opt_dic['user'] and password == cls.md5(opt_dic['user'],opt_dic['passwd']):
return {"result":True,"info":"登陆成功"}
else:
return {"result":False,"info":"登录失败"}
@classmethod
def myexit(cls,opt_dic):
return {'result':"myexit"}
class FTPServer(socketserver.BaseRequestHandler):
def handle(self):
while True:
opt_dic = self.myrecv()
# 通过反射调用Auth这个类中相应方法-*
if hasattr(Auth,opt_dic['operate']):
res = getattr(Auth,opt_dic['operate'])(opt_dic)
# print(res)
if res['result'] == "myexit":
return
# 发送给客户端用户处理结果
self.mysend(res)
# 等待用户操作第二轮界面内容
if res['result']:
opt_dic = self.myrecv()
if opt_dic['operate'] == "myexit":
return
# 走到这个位置越过了退出的情况,只剩下上传和下载了
if hasattr(self,opt_dic['operate']):
getattr(self,opt_dic["operate"])(opt_dic)
#{'operate': 'download', 'filename': 'ceshimovie.mp4'}
print(opt_dic)
else:
dic = {"result":False,"info":"没有该操作"}
self.mysend(dic)
def myrecv(self):
info = self.request.recv(1024)
opt_str = info.decode()
opt_dic = json.loads(opt_str)
return opt_dic
def mysend(self,send_info,sign=False):
send_info = json.dumps(send_info).encode()
if sign:
send_len = struct.pack("i",len(send_info))
#1发数据长度
self.request.send(send_len)
#2发具体数据
self.request.send(send_info)
def download(self,opt_dic):
# 先判断是否存在该文件
filename = opt_dic['filename']
file_abs = os.path.join(base_path,"video",filename)
if os.path.exists(file_abs):
# 1.先告诉用户,操作行为合法,存在该文件
dic = {"result":True,"info":"该文件存在"}
self.mysend(dic,True)
# 2.把文件大小和文件名传回去
filesize = os.path.getsize(file_abs)
dic = {"filename":filename,"filesize":filesize}
self.mysend(dic,True)
# 3.开始传文件内容
with open(file_abs,mode="rb") as fp:
while filesize:
# 最多读2048字节
content = fp.read(2048)
self.request.send(content)
filesize -= len(content)
print("服务端下载完毕")
else:
dic = {'result':False,"info":"文件不存在"}
self.mysend(dic)
myserver = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , FTPServer)
myserver.serve_forever()
client.py
import socket
import json
import struct
import os
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) )
# 收发数据逻辑
# ...
# 收数据
def myrecv(info_len=1024,sign = False):
if sign:
info_len = sk.recv(4)
# 返回元组
info_len = struct.unpack("i",info_len)[0]
file_info = sk.recv(info_len).decode()
file_dic = json.loads(file_info)
return file_dic
# 登录注册
def auth(opt):
'''
opt login or register
'''
usr = input("username: ").strip()
pwd = input("password: ").strip()
# 用户名,密码,操作行为
dic = {'user':usr,"passwd":pwd,"operate":opt}
str_dic = json.dumps(dic)
sk.send(str_dic.encode())
# 接受服务器返回的响应消息
return myrecv(info_len=1024)
# 注册
# res = auth("register")
# print(res)
def register():
res = auth("register")
return res
# 登录
# res = auth("beatdoudou")
# res = auth("login")
# print(res)
def login():
res = auth("login")
return res
# 退出
def myexit():
opt_dic = {"operate":"myexit"}
sk.send(json.dumps(opt_dic).encode())
exit("欢迎下次再来")
# myexit()
def download():
operate_dict = {
'operate':"download",
"filename":'ceshimovie.mp4'
}
opt_str = json.dumps(operate_dict)
sk.send(opt_str.encode("utf-8"))
# 接受服务给客户端是否文件存在的这样的判断
res = myrecv(sign=True)
# print(res)
if res['result']:
# 接受文件大小和文件名
dic = myrecv(sign=True)
# 创建文件夹
try:
os.mkdir("mydownload")
except:
pass
# 接受文件
# {'filename': 'ceshimovie.mp4', 'filesize': 20724134}
with open("./mydownload/"+dic['filename'],mode="wb") as fp:
while dic['filesize']:
content = sk.recv(2048)
fp.write(content)
dic['filesize'] -= len(content)
print("客户端下载完毕")
else:
print("没有该文件")
operate_lst1 = [("登录",login),("注册",register),("退出",myexit)]
operate_lst2 = [("下载",download),("退出",myexit)]
# 主函数进行统一调用
'''
(1, ('登录', <function login at 0x0000018DFB759E18>))
(2, ('注册', <function register at 0x0000018DFB759D90>))
(3, ('退出', <function myexit at 0x0000018DFB759EA0>))
'''
def main(operate_lst):
for i,tup in enumerate(operate_lst,start=1):
print(i,tup[0])
num = int(input("选择执行的序号>>>").strip())
# 直接调用对应的函数
res = operate_lst[num-1][1]() # login() register() myexit()
# 返回结果
return res
# res = main(operate_lst1)
# print(res)
# 循环调用
while True:
# 开启第一套操作界面
res = main(operate_lst1)
if res['result']:
res = main(operate_lst2)
print(res)
sk.close()