python實現ftp文件傳輸

         最近做了一個簡單的文件傳輸系統,基於ftp協議,使用python語言開發,雖然python裏面已經有ftplib模塊,可以很容易的實現ftp服務器。這次我使用的是socket實現client與ftp server之間的通訊和文件傳輸,client另起一個flask服務器,用於用戶在瀏覽器端的交互。系統實現的功能有:用戶登錄註冊,用戶查看ftp服務器端文件和下載上傳刪除操作,支持多進程、多用戶。

  一,登錄註冊

         該項目使用的是mongo數據庫,其實用戶登錄註冊功能很好實現,沒有什麼技術細節,這裏就略過了。數據庫管理代碼如下:

import pymongo
from pymongo.collection import Collection

class DBManager(object):

    def __init__(self):

        client = pymongo.MongoClient("mongodb://localhost:27017/")
        self.db = client["FTPDB"]
        self.users = self.db['user']

    #保存用戶登錄信息
    def saveUserInfo(self,account,password):

        users = self.users.find()
        for item in users:
            accountDB = item['account']
            if accountDB == account:
                return "false"


        data = []
        userInfo = {}
        userInfo['account'] = account
        userInfo['password'] = password
        data.append(userInfo)
        collection = Collection(self.db,"user")
        collection.insert(data)
        return "true"

    def confirmUserLoginInfo(self,account,password):

        users = self.users.find()
        '''
            result狀態: 1:表示初始狀態,即不存在用戶
                        2:表示存在該用戶、密碼不正確
                        3:驗證成功
        '''
        result = 1
        for item in users:
            accountDB = item['account']
            passwordDB = item['password']
            if accountDB == account:
                if passwordDB == password:
                    result = 3
                else:
                    result = 2
        return result

前端註冊js代碼如下:



function register() {
    account = $("#account").val();
    password = $("#password").val();
    confirmPassword = $("#confirmPassword").val();
    if(account == null || password == null || confirmPassword == null){
        alert("請先輸入必要信息")
        return;
    }
    if(password != confirmPassword){
        alert("密碼不一致");
        return;
    }
    var request = {
        type:"register",
        account:account,
        password:password
    }
    sendData("http://localhost:8080/register",request)
}

//向服務器發送數據
function sendData(url,json) {
    $.ajax({
        url: url, //請求的url地址
        dataType: "json", //返回格式爲json
        async: true, //請求是否異步,默認爲異步,這也是ajax重要特性
        data: json, //參數值
        type: "post", //請求方式
        success:function(data){
            //alert(data)
            if(data.toString() == "false"){
                alert("用戶名已存在");
            }else{
                window.location.href = "http://localhost:8080/index";
            }

        },
        error:function (error) {
            console.log(error);
        }
    });
}

二,文件管理(文件查看、刪除、上傳、下載)

客戶端與服務器端約定命令格式,服務器通過解析客戶端命令來執行操作。

server.py


from socket import *
import os,sys
import signal
import time

# 全局變量
HOST = '0.0.0.0'
PORT = 8686
ADDR = (HOST,PORT)
FILE_PATH = '../serverFiles/'

# 處理殭屍進程
signal.signal(signal.SIGCHLD,signal.SIG_IGN)

# 服務端功能類
class Server(object):

    def __init__(self):
        self.connfd = ""

    def do_list(self,account):
        # 獲取文件列表
        file_list = os.listdir(FILE_PATH+account)
        if not file_list:
            self.connfd.send("服務器文件庫爲空".encode())
            return
        else:
            self.connfd.send(b"OK")
            time.sleep(0.1)
        files = ""
        for file in file_list:
            if file[0] != '.' and os.path.isfile(FILE_PATH + account +"/"+ file):
                files += file + '#'
        self.connfd.send(files.encode())

    def delete(self,accout,fileName):
        os.remove(FILE_PATH + accout + "/" + fileName)
        self.connfd.send(b"OK")
        time.sleep(0.1)

    def do_get(self,account,filename):
        try:
            fd = open(FILE_PATH + account +"/"+ filename,'rb')
        except IOError:
            self.connfd.send("文件不存在".encode())
            return
        else:
            #print("發送OK")
            self.connfd.send(b'OK')
            time.sleep(0.1)
        # 發送文件內容
        while True:
            data = fd.read(1024)
            if not data:
                time.sleep(0.1)
                self.connfd.send(b'##')
                break
            #print("正在發送數據")
            self.connfd.send(data)
        fd.close()

    def do_put(self,account,filename):
        if os.path.exists(FILE_PATH + account +"/"+ filename):
            self.connfd.send('該文件已存在'.encode())
            return

        fd = open(FILE_PATH + account +"/"+ filename,'wb')
        self.connfd.send(b'OK')
        # 接收文件內容
        while True:
            data = self.connfd.recv(1024)
            if data == b'**':
                break
            fd.write(data)
        fd.close()

    def socket_tcp(self):
        s = socket()
        s.setsockopt(SOL_SOCKET,SO_REUSEADDR,True)
        s.bind(ADDR)
        s.listen(5)
        print("Listen the port 8686...")
        return s

    def do_request(self,connfd):
        self.connfd = connfd
        while True:
            data = connfd.recv(1024).decode()
            datas = data.split(' ')
            if not data or datas[1] == 'QUIT@#':
                connfd.close()
                return
            elif datas[1] == "LIST@#":
                #print("list")
                self.do_list(datas[0])
            elif datas[1] == 'GET@#':
                filename = datas[-1]
                self.do_get(datas[0],filename)
            elif datas[1] == 'PUT@#':
                filename = datas[-1]
                self.do_put(datas[0],filename)
            elif datas[1] == 'delete@#':
                filename = datas[-1]
                self.delete(datas[0],filename)

    def run(self):
        # 創建套接字
        s = self.socket_tcp()
        while True:
            try:
                connfd,addr = s.accept()
            except KeyboardInterrupt:
                sys.exit("服務器退出")
            except Exception as e:
                print(e)
                continue
            print("Connect from",addr)

            # 創建子進程
            pid = os.fork()
            if pid == 0:
                s.close()
                self.do_request(connfd) #處理客戶端具體請求
                os._exit(0)
            else:
                connfd.close()

if __name__ == "__main__":
    server = Server()
    server.run()

client.py

"""
    client.py
"""
import socket
import os,sys
import time

# 服務器地址
ADDR = ("127.0.0.1",8686)
FILE_PATH = "./clientFiles/"
# 客戶端功能類
class Client(object):
    def __init__(self,account):
        self.sockfd = ""
        self.account = account

    #獲得服務器文件列表
    def server_list(self):
        ftpServerFiles = []
        self.sockfd.send((self.account+' LIST@# ').encode())
        # 等待回覆
        data = self.sockfd.recv(128).decode()
        if data == "OK":
            files = self.sockfd.recv(4096).decode()
            for file in files.split('#'):
                #print(file)
                ftpServerFiles.append(file)
        else:
            # 無法完成操作
            print(data)
        ftpServerFiles = ftpServerFiles[:-1]
        return ftpServerFiles

    #獲得用戶文件夾列表
    def client_list(self):
        # 獲取文件列表
        userFiles = []
        file_list = os.listdir(FILE_PATH+self.account+"/")
        if not file_list:
            return
        else:
            time.sleep(0.1)
        files = ""
        for file in file_list:
            if file[0] != '.' and os.path.isfile(FILE_PATH + self.account + "/" + file):
                userFiles.append(file)
        return userFiles

    #退出
    def do_quit(self):
        self.sockfd.send((self.account+' QUIT@# ').encode())
        self.sockfd.close()
        sys.exit('謝謝使用')

    #用戶下載服務器文件
    def do_get(self,filename):
        self.sockfd.send((self.account+' GET@# '+filename).encode())
        data = self.sockfd.recv(128).decode()

        if data == 'OK':
            fd = open(FILE_PATH + self.account + "/" + filename,'wb')
            #複寫
            while True:
                data = self.sockfd.recv(1024)
                if data == b'##':
                    #print("##")
                    break
                #print("正在寫入數據")
                fd.write(data)
            fd.close()
        else:
            print(data)
    
    #用戶將文件上傳到服務器文件夾
    def do_put(self,filename):
        try:
            fd = open(FILE_PATH + self.account + "/" + filename,'rb')
        except IOError:
            print("文件不存在")
            return

        # 獲取文件名
        filename = filename.split('/')[-1]

        # else:
        self.sockfd.send((self.account+' PUT@# '+filename).encode())
        data = self.sockfd.recv(128).decode()
        # 發送文件
        if data == 'OK':
            while True:
                data = fd.read(1024)
                if not data:
                    time.sleep(0.1)
                    self.sockfd.send(b'**')
                    break
                self.sockfd.send(data)
            fd.close()
            return "true"
        else:
            print(data)
            return "false"
    #刪除用戶文件
    def removeU(self,fileName):
        os.remove(FILE_PATH + self.account + "/" + fileName)
        return "true"
    #刪除用戶文件
    def removeF(self,fileName):
        self.sockfd.send((self.account+' delete@# '+fileName).encode())
        # 等待回覆
        data = self.sockfd.recv(128).decode()
        if data == "OK":
            return "true"

    def menu_display(self):
        print("\n------命令選擇-------")
        print("***    clist      ***")
        print("***    slist      ***")
        print("***  get list     ***")
        print("***  put list     ***")
        print("***    quit       ***")
        print("----------------------")

    def run(self,cmd):
        # 創建套接字
        sockfd = socket.socket()
        try:
            sockfd.connect(ADDR)
        except Exception as e:
            print(e)
            return
        result = ""
        self.sockfd = sockfd
        # choice(cmd,ftp)
        if cmd == "slist":
            result = self.server_list()
            return result
        elif cmd == "clist":
            result = self.client_list()
            return result
        elif cmd =='quit':
            self.do_quit()
        elif cmd[:3] == 'get':
            filename = cmd.strip().split(' ')[-1]
            self.do_get(filename)
        elif cmd[:3] == 'put':
            filename = cmd.strip().split(' ')[-1]
            result = self.do_put(filename)
            return result
        elif cmd[:7] == 'removeU':
            filename = cmd.strip().split(' ')[-1]
            self.removeU(filename)
        elif cmd[:7] == 'removeF':
            filename = cmd.strip().split(' ')[-1]
            self.removeF(filename)
        else:
            print("請輸入正確命令!")


if __name__ == "__main__":
    client = Client("ffy")
    client.run()








運行界面:

 源碼出售:5元

------------------------------------------------------更新分界線-------------------------------------------------------------------------

 代碼我是在ubuntu系統寫的,當時運行沒出現啥問題,但當代碼移植到windows上時就出現了問題,因爲多進程使用的os.fork()創建,這是linux系統獨有的,對於windows需要更換一種表示方法。server.py代碼更新如下


from socket import *
import os,sys
import signal
import time
from multiprocessing import Process

# 全局變量
HOST = '0.0.0.0'
PORT = 8686
ADDR = (HOST,PORT)
FILE_PATH = '../serverFiles/'

# 處理殭屍進程
# signal.signal(signal.SIGCHLD,signal.SIG_IGN)

# 服務端功能類
class Server(object):

    def __init__(self):
        self.connfd = ""

    def do_list(self,account):
        # 獲取文件列表
        file_list = os.listdir(FILE_PATH+account)
        if not file_list:
            self.connfd.send("服務器文件庫爲空".encode())
            return
        else:
            self.connfd.send(b"OK")
            time.sleep(0.1)
        files = ""
        for file in file_list:
            if file[0] != '.' and os.path.isfile(FILE_PATH + account +"/"+ file):
                files += file + '#'
        self.connfd.send(files.encode())

    def delete(self,accout,fileName):
        os.remove(FILE_PATH + accout + "/" + fileName)
        self.connfd.send(b"OK")
        time.sleep(0.1)

    def do_get(self,account,filename):
        try:
            fd = open(FILE_PATH + account +"/"+ filename,'rb')
        except IOError:
            self.connfd.send("文件不存在".encode())
            return
        else:
            #print("發送OK")
            self.connfd.send(b'OK')
            time.sleep(0.1)
        # 發送文件內容
        while True:
            data = fd.read(1024)
            if not data:
                time.sleep(0.1)
                self.connfd.send(b'##')
                break
            #print("正在發送數據")
            self.connfd.send(data)
        fd.close()

    def do_put(self,account,filename):
        if os.path.exists(FILE_PATH + account +"/"+ filename):
            self.connfd.send('該文件已存在'.encode())
            return

        fd = open(FILE_PATH + account +"/"+ filename,'wb')
        self.connfd.send(b'OK')
        # 接收文件內容
        while True:
            data = self.connfd.recv(1024)
            if data == b'**':
                break
            fd.write(data)
        fd.close()

    def socket_tcp(self):
        s = socket()
        s.setsockopt(SOL_SOCKET,SO_REUSEADDR,True)
        s.bind(ADDR)
        s.listen(5)
        print("Listen the port 8686...")
        return s

    def do_request(self,connfd):
        self.connfd = connfd
        while True:
            data = connfd.recv(1024).decode()
            datas = data.split(' ')
            if not data or datas[1] == 'QUIT@#':
                connfd.close()
                return
            elif datas[1] == "LIST@#":
                #print("list")
                self.do_list(datas[0])
            elif datas[1] == 'GET@#':
                filename = datas[-1]
                self.do_get(datas[0],filename)
            elif datas[1] == 'PUT@#':
                filename = datas[-1]
                self.do_put(datas[0],filename)
            elif datas[1] == 'delete@#':
                filename = datas[-1]
                self.delete(datas[0],filename)

    def run(self):
        # 創建套接字
        s = self.socket_tcp()
        while True:
            try:
                connfd,addr = s.accept()
            except KeyboardInterrupt:
                sys.exit("服務器退出")
            except Exception as e:
                print(e)
                continue
            print("Connect from",addr)

            # 創建子進程
            p = Process(target=self.do_request(connfd), args=('children process', ))
            p.start()

if __name__ == "__main__":
    server = Server()
    server.run()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章