python實現(xiàn)可以斷點續(xù)傳和并發(fā)的ftp程序
前言
下載文件時,最怕中途斷線,無法成功下載完整的文件。斷點續(xù)傳就是從文件中斷的地方接下去下載,而不必重新下載。這項功能對于下載較大文件時非常有用。那么這篇文章就來給大家分享如何利用python實現(xiàn)可以斷點續(xù)傳和并發(fā)的ftp程序。
一、要求
1、用戶md5認證
2、支持多用戶同時登陸(并發(fā))
3、進入用戶的命令行模式,支持cd切換目錄,ls查看目錄子文件
4、執(zhí)行命令(ipconfig)
5、傳輸文件:
a、支持斷點續(xù)傳
b、傳輸中顯示進度條
二、思路
1.客戶端用戶登錄和注冊:
a、客戶端僅提供用戶名和密碼,選擇登錄或注冊,
b、服務(wù)器端進行注冊并將加密后的密碼寫入文件,最后返回給客戶端是否登錄或注冊成功
2.ls和cd命令
a、客戶端輸入命令,服務(wù)器端處理并返回給客戶端
3.執(zhí)行命令:
a、客戶端發(fā)送需要執(zhí)行的命令
b、服務(wù)器端執(zhí)行命令,并返回客戶端需要接收該命令的次數(shù)s=r[0]+1,其中r=divmod(結(jié)果總長度,1024)
c、客戶端收到次數(shù),告訴服務(wù)端已經(jīng)收到
d、服務(wù)端發(fā)送執(zhí)行結(jié)果,客戶端進行for循環(huán)接收該結(jié)果
4.發(fā)送文件:
a、客戶端輸入文件路徑(測試版路徑為:f.png),發(fā)送文件名和文件大小
b、服務(wù)器端檢測指定目錄是否含有該文件,如果沒有,返回給客戶端字符串s,即從頭開始發(fā)送start,has_recv=0
如果有,即需要斷點續(xù)傳,返回給客戶端已經(jīng)上傳了多少has_recv
c、客戶端接收返回值,并seek到has_recv的位置,進行循環(huán)收發(fā),打印當前進度,直到傳輸完畢。
注:本程序可循環(huán)接收用戶選擇傳輸文件和執(zhí)行命令
三、代碼
配置文件:
#!/usr/bin/env python # -*- coding: utf-8 -*- import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) #配置文件的上層目錄 NEW_FILENAME=os.path.join(BASE_DIR,'view') #新文件目錄 NAME_PWD=os.path.join(BASE_DIR,'db','name_pwd') #用戶名和密碼目錄 USER_FILE=os.path.join(BASE_DIR,'db')
服務(wù)器端:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys,os
import time
import socket
import hashlib
import pickle
import subprocess
import socketserver
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from config import settings
new=settings.NEW_FILENAME
class Myserver(socketserver.BaseRequestHandler):
def recv_file(self):
'''
文件傳輸
:return:
'''
conn=self.request
a=str(conn.recv(1024),encoding='utf-8')
file_size,file_name=a.split(',')
new_file_name=os.path.join(new,file_name)
if file_name in new: #檢測文件是否已存在,涉及斷點續(xù)傳
has_recv=os.stat(new).st_size #計算臨時文件大小
conn.sendall(bytes(has_recv,encoding='utf-8'))
with open(new_file_name,'ab') as f: #追加模式
while has_recv<=int(file_size):
data=conn.recv(1024)
f.write(data)
has_recv+=len(data)
else:
has_recv=0
conn.sendall(bytes('s',encoding='utf-8')) # 客戶端收到字符串s,從0開始發(fā)送
with open(new_file_name,'wb') as f:
while has_recv<=int(file_size):
data=conn.recv(1024)
f.write(data)
has_recv+=len(data)
def command(self):
'''
執(zhí)行命令
:return:
'''
conn=self.request
a=conn.recv(1024)
ret=str(a,encoding='utf-8')
ret2 = subprocess.check_output(ret, shell=True)
r=divmod(len(ret2),1024)
s=r[0]+1 #客戶端需要接收的次數(shù)
conn.sendall(bytes(str(s),encoding='utf-8'))
conn.recv(1024) #確認客戶端收到需要接收的次數(shù)
conn.sendall(ret2)
def md5(self,pwd):
'''
對密碼進行加密
:param pwd: 密碼
:return:
'''
hash=hashlib.md5(bytes('xx7',encoding='utf-8'))
hash.update(bytes(pwd,encoding='utf-8'))
return hash.hexdigest()
def login(self,usrname,pwd):
'''
登陸
:param usrname: 用戶名
:param pwd: 密碼
:return:是否登陸成功
'''
conn=self.request
s=pickle.load(open(settings.NAME_PWD,'rb'))
if usrname in s:
if s[usrname]==self.md5(pwd): #和加密后的密碼進行比較
return True
else:
return False
else:
return False
def regist(self,usrname,pwd):
'''
注冊
:param usrname: 用戶名
:param pwd: 密碼
:return:是否注冊成功
'''
conn=self.request
s=pickle.load(open(settings.NAME_PWD,'rb'))
if usrname in s:
return False
else:
s[usrname]=self.md5(pwd)
mulu=os.path.join(settings.USER_FILE,usrname)
os.makedirs(mulu,'a')
pickle.dump(s,open(settings.NAME_PWD,'wb'))
return True
def before(self,usrname,pwd,ret):
'''
判斷注冊和登陸,并展示用戶的詳細目錄信息,支持cd和ls命令
:return:
'''
conn=self.request
if ret=='1':
r=self.login(usrname,pwd)
if r:
conn.sendall(bytes('y',encoding='utf-8'))
else:
conn.sendall(bytes('n',encoding='utf-8'))
elif ret=='2':
# print(usrname,pwd)
r=self.regist(usrname,pwd)
if r:
conn.sendall(bytes('y',encoding='utf-8'))
else:
conn.sendall(bytes('n',encoding='utf-8'))
def usr_file(self,usrname):
'''
展示用戶的詳細目錄信息,支持cd和ls命令
:param usrname: 用戶名
:return:
'''
conn=self.request
conn.recv(1024)
mulu=os.path.join(settings.USER_FILE,usrname)
conn.sendall(bytes(mulu,encoding='utf-8'))
while True:
b=conn.recv(1024)
ret=str(b,encoding='utf-8')
try:
a,b=ret.split(' ',1)
except Exception as e:
a=ret
if a=='cd':
if b=='..':
mulu=os.path.dirname(mulu)
else:
mulu=os.path.join(mulu,b)
conn.sendall(bytes(mulu,encoding='utf-8'))
elif a=='ls':
ls=os.listdir(mulu)
print(ls)
a=','.join(ls)
conn.sendall(bytes(a,encoding='utf-8'))
elif a=='q':
break
def handle(self):
conn=self.request
conn.sendall(bytes('welcome',encoding='utf-8'))
b=conn.recv(1024)
ret=str(b,encoding='utf-8')
print(ret)
conn.sendall(bytes('b ok',encoding='utf-8'))
c=conn.recv(1024)
r=str(c,encoding='utf-8')
usrname,pwd=r.split(',')
self.before(usrname,pwd,ret) #登陸或注冊驗證
self.usr_file(usrname) #展示用戶的詳細目錄信息,支持cd和ls命令
while True:
a=conn.recv(1024)
conn.sendall(bytes('收到a',encoding='utf-8'))
ret=str(a,encoding='utf-8')
if ret=='1':
self.recv_file()
# conn.sendall(bytes('file ok',encoding='utf-8'))
elif ret=='2':
self.command()
elif ret=='q':
break
else:
pass
if __name__=='__main__':
sever=socketserver.ThreadingTCPServer(('127.0.0.1',9999),Myserver)
sever.serve_forever()
客戶端:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import time
import os
import socket
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from config import settings
def send_file(file_path):
'''
發(fā)送文件
:param file_name:文件名
:return:
'''
size=os.stat(file_path).st_size
file_name=os.path.basename(file_path)
obj.sendall(bytes(str(size)+','+file_name,encoding='utf-8')) #發(fā)送文件大小和文件名
ret=obj.recv(1024) #接收已經(jīng)傳了多少
r=str(ret,encoding='utf-8')
if r=='s': #文件不存在,從頭開始傳
has_send=0
else: #文件存在
has_send=int(r)
with open(file_path,'rb') as f:
f.seek(has_send) #定位到已經(jīng)傳到的位置
while has_send<size:
data=f.read(1024)
obj.sendall(data)
has_send+=len(data)
sys.stdout.write('\r') #清空文件內(nèi)容
time.sleep(0.2)
sys.stdout.write('已發(fā)送%s%%|%s' %(int(has_send/size*100),(round(has_send/size*40)*'★')))
sys.stdout.flush() #強制刷出內(nèi)存
print("上傳成功\n")
def command(command_name):
'''
執(zhí)行命令
:param command_name:
:return:
'''
obj.sendall(bytes(command_name,encoding='utf-8'))
ret=obj.recv(1024) #接收命令需要接收的次數(shù)
obj.sendall(bytes('收到次數(shù)',encoding='utf-8'))
r=str(ret,encoding='utf-8')
for i in range(int(r)): #共需要接收int(r)次
ret=obj.recv(1024) #等待客戶端發(fā)送
r=str(ret,encoding='GBK')
print(r)
def login(usrname,pwd):
'''
登陸
:param usrname:用戶名
:param pwd:密碼
:return:是否登陸成功
'''
obj.sendall(bytes(usrname+','+pwd,encoding='utf-8'))
ret=obj.recv(1024)
r=str(ret,encoding='utf-8')
if r=='y':
return 1
else:
return 0
def regist(usrname,pwd):
'''
注冊
:param usrname:用戶名
:param pwd:密碼
:return:是否注冊成功
'''
obj.sendall(bytes(usrname+','+pwd,encoding='utf-8'))
ret=obj.recv(1024)
r=str(ret,encoding='utf-8')
if r=='y':
return 1
else:
return 0
def before(usrname,pwd):
'''
選擇登陸或注冊,展示用戶的詳細目錄信息,支持cd和ls命令
:return:
'''
a=input('請選擇1.登陸 2.注冊')
obj.sendall(bytes(a,encoding='utf-8'))
obj.recv(1024)
if a=='1':
ret=login(usrname,pwd)
if ret:
print('登陸成功')
return 1
else:
print('用戶名或密碼錯誤')
return 0
elif a=='2':
ret=regist(usrname,pwd)
if ret:
print('注冊成功')
return 1
else:
print('用戶名已存在')
return 0
def usr_file(usrname):
obj.sendall(bytes('打印用戶文件路徑',encoding='utf-8'))
ret=obj.recv(1024) #等待客戶端發(fā)送
r=str(ret,encoding='utf-8')
print(r)
while True:
a=input('輸入cd切換目錄,ls查看目錄詳細信息,q退出>:')
obj.sendall(bytes(a,encoding='utf-8'))
if a=='q':
break
else:
ret=obj.recv(1024) #等待客戶端發(fā)送
r=str(ret,encoding='utf-8')
if len(r)==1:#判斷是cd結(jié)果還是ls的結(jié)果(ls只有一個子目錄也可以直接打印)
print(r)
else:
li=r.split(',')
for i in li:
print(i) #打印每一個子目錄
def main(usrname,pwd):
ret=obj.recv(1024) #等待客戶端發(fā)送
r=str(ret,encoding='utf-8')
print(r)
result=before(usrname,pwd)#登陸或注冊
if result:
usr_file(usrname)
while True:
a=input('請選擇1.傳文件 2.執(zhí)行命令 q退出:')
obj.sendall(bytes(str(a),encoding='utf-8'))
ret=obj.recv(1024) #確認是否收到a
r=str(ret,encoding='utf-8')
print(r)
if a=='1':
b=input('請輸入文件路徑(測試版路徑為:f.png):')
# b='f.png'
if os.path.exists(b):
send_file(b)
obj.sendall(bytes('hhe',encoding='utf-8'))
# obj.recv(1024)
elif a=='2':
b=input('請輸入command:')
command(b)
elif a=='q':
break
else:
print('輸入錯誤')
obj.close()
if __name__ == '__main__':
obj=socket.socket() #創(chuàng)建客戶端socket對象
obj.connect(('127.0.0.1',9999))
usrname=input('請輸入用戶名')
pwd=input('請輸入密碼')
main(usrname,pwd)
總結(jié)
以上就是python實現(xiàn)可以斷點續(xù)傳和并發(fā)的ftp程序的全部內(nèi)容,文章介紹的很詳細,希望對大家學習或者使用python帶來一定的幫助。
相關(guān)文章
利用Python輕松實現(xiàn)視頻轉(zhuǎn)GIF動圖
在看視頻的時候覺得某段非常有意思想弄成動圖,但是無從下手!本文就將介紹如何利用Python搞定這一需求,感興趣的小伙伴可以學習一下2022-01-01
Python實現(xiàn)生成簡單的Makefile文件代碼示例
這篇文章主要介紹了Python實現(xiàn)生成簡單的Makefile文件代碼示例,本文給出了兩段實現(xiàn)代碼,需要的朋友可以參考下2015-03-03
Python3從零開始搭建一個語音對話機器人的實現(xiàn)
這篇文章主要介紹了Python3從零開始搭建一個語音對話機器人的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08

