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()

 

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