FTPserver

Posted chengege

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FTPserver相关的知识,希望对你有一定的参考价值。

技术分享图片

客户端代码:

import os
import hashlib

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

FILE_PATH = os.path.join(BASE_DIR, donwload)

def make_md5_on_file(file_path):
    ‘‘‘给文件制作md5  每次都要打开文件,要想办法保存到一个位置,当文件发生修改时更新这个md5‘‘‘
    m = hashlib.md5()
    with open(file_path, rb) as f:
        for line in f:
            bytes_str = line
            m.update(bytes_str)
        md5value = m.hexdigest()
        return md5value
技术分享图片
import socket
import optparse
import json
import struct
import os
import sys
import shelve
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import settings


class FTPClient(object):
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    allow_reuse_address = True
    max_pack_size = 8192
    request_queue_size = 5
    coding = utf-8
    donwload_dir = settings.FILE_PATH

    def __init__(self, connect=True):
        ‘‘‘ elf.options 这是这个模块生成的一个字典 self.args  这是用户输入的命令。和sys.argv 差不多
            如果用户按照规则输入,那么相应的值 就会在字典中显示, 不按规则输入的值,就放倒了列表中‘‘‘
        parser = optparse.OptionParser()
        parser.add_option("-s", "--server", dest=server, help="ftp server ip_addr")
        parser.add_option("-P", "--port", type="int", dest="port", help="ftp server port")
        parser.add_option("-u", "--username", dest="username", help="username info")
        parser.add_option("-p", "--password", dest="password", help="password info")
        self.options, self.args = parser.parse_args()
        self.current_dir = None
        self.argv_verification()  # 判断合法性
        self.socket = self.make_connection()  # 建立连接
        self.shelve_obj = shelve.open(defect_file)  # 得到下载时shelve文件对象
        self.shelve_load_obj = shelve.open(unfinish_file)
        if connect:
            try:
                self.client_connect()
            except Exception:
                self.client_close()
                exit(以断开连接)

    def argv_verification(self):
        ‘‘‘判断用户输入的合法性‘‘‘
        if not self.options.server or not self.options.port:
            exit(必须提供,ip 和 端口)

    def make_connection(self):
        ‘‘‘由构造函数调用生成 socket 对象‘‘‘
        self.socket = socket.socket(self.address_family, self.socket_type)
        return self.socket

    def client_connect(self):
        ‘‘‘由构造函数直接调用,激活客户端,连接服务端‘‘‘
        self.socket.connect((self.options.server, self.options.port))  #

    def client_close(self):
        ‘‘‘由构造函数调用,关闭客户端‘‘‘
        self.socket.close()

    def auth(self):
        ‘‘‘输入账户名,密码。服务端验证完成后,根据接收到的状态码,返回 Ture or False‘‘‘
        count = 0
        while count < 3:
            username = input(username:).strip()
            if not username: continue
            self.current_dir = \\ + username
            pwd = input(password:).strip()
            cmd = {
                action_type: auth,
                username: username,
                password: pwd
            }

            self.send_head(cmd)
            head_dic = self.recv_head()
            if head_dic[status_code] == 200:

                return True
            else:
                count += 1
                print(head_dic[status_msg])
                continue

    def send_head(self, cmd):
        ‘‘‘发送客户端的报头,‘‘‘
        client_head_bytes = json.dumps(cmd).encode(self.coding)
        client_head_len = struct.pack(i, len(client_head_bytes))
        self.socket.send(client_head_len)
        self.socket.send(client_head_bytes)

    def recv_head(self):
        ‘‘‘接收服务端发来的报头‘‘‘
        obj = self.socket.recv(4)
        header_size = struct.unpack(i, obj)[0]
        header_json = self.socket.recv(header_size).decode(self.coding)
        header_dict = json.loads(header_json)
        return header_dict

    def send_file(self, file_path, head_dic, sercver_dir, md5val, send_size=0):
        ‘‘‘发送文件‘‘‘
        self.shelve_load_obj[file_path] = [head_dic, sercver_dir, md5val]
        genertor = self.progress_bar(head_dic[filesize])
        genertor.__next__()
        with open(file_path, rb) as f:
            f.seek(send_size)
            for line in f:
                self.socket.send(line)
                send_size += len(line)
                genertor.send(send_size)
            else:
                del self.shelve_load_obj[file_path]
                print(数据发送成功)
        header_dict = self.recv_head()
        print(header_dict[status_msg])

    def recv_file(self, file_path, file_size, md5_value, filename, recv_size=0):
        ‘‘‘接收服务端文件数据, 并 保存所有文件信息, 防止程序中断时,可以进行续传,
        文件正常传完, 删除文件描述信息, 并改名‘‘‘
        file_abs_path = os.path.join(r%s\\%s % (self.current_dir, filename))  # 拼接客户端在那个位置下载的文件
        self.shelve_obj[file_abs_path] = [file_size, filename+.download]  # 以绝对路径为键,保存文件大小的值

        with open(file_path, ab) as f:
            while recv_size < file_size:
                line = self.socket.recv(self.max_pack_size)
                f.write(line)
                recv_size += len(line)
                progres(recv_size, file_size)  # 进度条
        md5value = settings.make_md5_on_file(file_path)
        if md5value == md5_value:
            del self.shelve_obj[file_abs_path]
            if filename not in os.listdir(self.donwload_dir):
                os.renames(file_path, os.path.join(self.donwload_dir, filename))
            else:
                print(文件重名,未覆盖!, end=‘‘)
            print()

    def put(self, *args):
        ‘‘‘上传文件‘‘‘
        cmd = args[0]
        filename = args[1]
        sercver_dir = %s\\%s % (self.current_dir, filename)
        if not os.path.isfile(os.path.join(self.donwload_dir, filename)):
            print(file %s 不在donwload文件中 % filename)
            return
        else:
            filesize = os.path.getsize(os.path.join(self.donwload_dir, filename))
            md5value = settings.make_md5_on_file(os.path.join(self.donwload_dir, filename))
            head_dic = {action_type: cmd, filesize: filesize, filename: filename, md5value: md5value}
            file_path = os.path.join(self.donwload_dir, filename)
            self.send_head(head_dic)
            self.send_file(file_path, head_dic, sercver_dir, md5value)

    def re_put(self, file_path, load_head_dic, sercver_dir, md5value):
        server_head_dic = self.recv_head()
        received_size = server_head_dic[file_size]
        self.send_file(file_path, load_head_dic, sercver_dir, md5value, send_size=received_size)

    def progress_bar(self, total_size):
        ‘‘‘生成器 方式的, 进度条展示‘‘‘
        current_percent = 0
        last_percent = 0
        while True:
            recvived_size = yield current_percent
            current_percent = int(recvived_size / total_size * 100)
            if current_percent > last_percent:
                print("#" * int(current_percent/2) + "{percent}%".format(percent=current_percent), flush=True, end=
)
                last_percent = current_percent

    def get(self, *args):
        ‘‘‘下载文件‘‘‘
        cmd = args[0]
        filename = args[1]
        client_head_dic = {action_type: cmd, filename: filename}
        self.send_head(client_head_dic)
        server_head_dic = self.recv_head()

        if server_head_dic[status_code] == 300:
            print(server_head_dic[status_msg])
            return

        file_path = os.path.join(self.donwload_dir, %s.download % filename)  # 存放文件路径
        total_size = server_head_dic[filesize]  # 文件最大大小
        md5_val = server_head_dic[md5value]  # 得到服务端发来的 MD5 值
        self.recv_file(file_path, total_size, md5_val, filename)

    def re_get(self, head_dic):
        ‘‘‘由程序自检之后,用户选择 是否进行续传。 如果续传 调用该方法。
        发送报头:
        1. 检测 已收到文件大小
        2. 编辑报头,使用 self.socket.send() 发送报头
        3. 接收文件,‘‘‘
        self.send_head(head_dic)
        server_head_dic = self.recv_head()
        if server_head_dic[status_code] == 300:
            print(server_head_dic[status_msg])
            return
        file_path = os.path.join(self.donwload_dir, head_dic[filename])  # 存放文件路径
        total_size = head_dic[total_size]
        md5_val = server_head_dic[md5value]  # 得到服务端发来的 MD5 值
        self.recv_file(file_path, total_size, md5_val, server_head_dic[filename], head_dic[received_file_size])

    def ls(self, *args):
        ‘‘‘显示当前目录下的文件‘‘‘
        cmd = args[0]
        client_head_dic = {action_type: cmd}
        self.send_head(client_head_dic)
        server_head_dic = self.recv_head()
        if server_head_dic[status_code] == 0:
            print(server_head_dic[dir_info])
        if server_head_dic[status_code] == 100:
            print(server_head_dic[dir_info])

    def cd(self, *args):
        ‘‘‘切换目录‘‘‘
        cmd = args[0]
        dirname = args[1]
        if dirname != .:
            client_head_dic = {action_type: cmd, dirname: dirname}
            self.send_head(client_head_dic)
            server_head_dic = self.recv_head()
            if server_head_dic[status_code] == 400:
                print(server_head_dic[status_msg])
            elif server_head_dic[status_code] == 401:
                print(server_head_dic[status_msg])
                self.current_dir = server_head_dic[dirn]
            elif server_head_dic[status_code] == 0:
                self.current_dir = server_head_dic[dirn]
        else:
            print(输入错误)
            return

    def help_msg(self, *args):
        msg = ‘‘‘command error!!!
        Correct format:
        get filename 下载文件
        put filename 上传文件
        ls  显示当前所在目录下的文件和子目录
        cd dirname 切换到那个目录下‘‘‘
        print(msg)

    def unfinished_file_check(self):
        if not self.shelve_obj:
            print(没有未接收的文件.center(30, -))
            return
        for index, abs_file in enumerate(self.shelve_obj.keys()):
            self.received_file_size = os.path.getsize(os.path.join(self.donwload_dir, self.shelve_obj[abs_file][1]))
            print(文件编号 %d  服务器文件保存地址 %s  文件总大小%s  文件名%s  已收到文件大小%s %
                  (index, abs_file, self.shelve_obj[abs_file][0], self.shelve_obj[abs_file][1], self.received_file_size))
        while True:
            choice = input(选择想要继续下载的文件编号 [back] 退出:).strip()
            if not choice: continue
            if choice == back: break
            if choice.isdigit():
                choice = int(choice)
                if choice >=0 and choice <= index:
                    selected_file = list(self.shelve_obj.keys())[choice]  # 通过索引,拿到想要的那个文件的 key
                    total_size = self.shelve_obj[selected_file][0]  # 文件总大小
                    file_name = self.shelve_obj[selected_file][1]  # 文件名
                    size = self.received_file_size  # 已收到的文件大小
                    head_dic = {action_type: re_get, abs_file: selected_file,  total_size: total_size,
                                received_file_size: size, filename: file_name}
                    self.re_get(head_dic)

    def unsend_by_file_check(self):
        if not self.shelve_load_obj:
            print(没有未上传完整的文件.center(30, -))
            return
        for index, abs_file in enumerate(self.shelve_load_obj.keys()):
            print(%s %s %s % (index, abs_file, self.shelve_load_obj[abs_file]))
        while True:
            choice = input(选择想要继续上传的文件编号 [back] 退出:).strip()
            if not choice: continue
            if choice == back: break
            if choice.isdigit():
                choice = int(choice)
                if choice >= 0 and choice <= index:
                    # file_path, head_dic, sercver_dir, md5val
                    file_path = list(self.shelve_load_obj.keys())[choice]
                    filesize = self.shelve_load_obj[file_path][0][filesize]
                    load_head_dic = self.shelve_load_obj[file_path][0]
                    sercver_dir = self.shelve_load_obj[file_path][1].strip(\\)
                    md5value = self.shelve_load_obj[file_path][2]
                    head_dic = {action_type: re_put, abs_file: sercver_dir, md5value: md5value, filesize: filesize}
                    self.send_head(head_dic)
                    self.re_put(file_path, load_head_dic, sercver_dir, md5value)

    def interactive(self):  # 交互函数
        ‘‘‘处理 与 服务端 的所有交互,解析用户输入的指令‘‘‘
        if self.auth():
            self.unfinished_file_check()
            self.unsend_by_file_check()
            while True:
                inp = input([%s] Q退出: % self.current_dir).strip()
                if not inp: continue
                cmds = inp.split()
                if inp != Q:
                    if hasattr(self, cmds[0]):
                        func = getattr(self, cmds[0])
                        func(*cmds)
                    else:
                        self.help_msg()
                        continue
                else:
                    self.shelve_obj.close()
                    self.client_close()
                    exit(谢谢使用)

    def make_dir(self, *args):
        ‘‘‘在自己的目录下,创建文件夹‘‘‘
        print(在自己的目录下,创建文件夹)

def humanbytes(B):
    ‘‘‘这传代码 抄来的  T_T ‘‘‘
    B = float(B)
    KB = float(1024)
    MB = float(KB ** 2)  # 1,048,576
    GB = float(KB ** 3)  # 1,073,741,824
    TB = float(KB ** 4)  # 1,099,511,627,776

    if B < KB:
        return {0} {1}.format(B,Bytes if 0 == B > 1 else Byte)
    elif KB <= B < MB:
        return {0:.2f} KB.format(B/KB)
    elif MB <= B < GB:
        return {0:.2f} MB.format(B/MB)
    elif GB <= B < TB:
        return {0:.2f} GB.format(B/GB)
    elif TB <= B:
        return {0:.2f} TB.format(B/TB)

def progres(recv_size, total_size):
    bar_length = 50
    percent = float(recv_size) / float(total_size)
    hashes = = * int(percent * bar_length)
    spaces =   * (bar_length - len(hashes))

    sys.stdout.write("
传输中: [%s] %d%%  %s/%s " % (hashes + spaces, percent * 100,
                                                   humanbytes(recv_size), humanbytes(total_size)))
    sys.stdout.flush()


if __name__ == __main__:
    client = FTPClient()
    client.interactive()
client.py

服务端代码:

import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 入口程序必须先 指定 基础目录。以便于其余的调用程序,可以通过这个路径,进行相对的导入
sys.path.append(BASE_DIR)  # 将基础目录添加到,路径列表中

if __name__ == __main__:
    from core import management
    # print()
    # [‘luffy_server.py‘, ‘start‘]
    # sys.argv 把用户终端输入的命令,拿到并把拿到的列表,交给 解析命令的类。传给__init__了
   #[luffy_server.py‘, start]
argv_parser = management.ManagementTool(sys.argv) argv_parser.execute()
技术分享图片
import os
import socket

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

HOST = "127.0.0.1"  #  服务端IP 用于服务端进行绑定使用
PORT = 8080  # 服务端 端口

ADDRESS_FAMILY = socket.AF_INET
SOCKET_TYPE = socket.SOCK_STREAM
ALLOW_REUSE_ADDRESS = True
MAX_PACKET_SIZE = 8192
MAX_SOCKET_LISTEN = 5
CODING = utf-8  # 指定编码类型
USER_HOME_DIR = os.path.join(BASE_DIR, home)

ACCOUNT_FILE = os.path.join(BASE_DIR, conf, accounts.ini)
settings
技术分享图片
import hashlib
import configparser
from .import settings

def make_md5_on_file(file_path):
    ‘‘‘给文件制作md5  每次都要打开文件,要想办法保存到一个位置,当文件发生修改时更新这个md5‘‘‘
    m = hashlib.md5()
    with open(file_path, rb) as f:
        for line in f:
            bytes_str = line
            m.update(bytes_str)
        md5value = m.hexdigest()
        return md5value

def load_all_user_info():
    ‘‘‘由FTPserver调用,加载所有的用户数据到内存‘‘‘
    confing = configparser.ConfigParser()
    confing.read(settings.ACCOUNT_FILE)
    return confing
conf_tool.py
技术分享图片
from core import main

class ManagementTool(object):
    ‘‘‘负责对 用户输入的指令,进行解析。并调用相应的模块处理‘‘‘
    def __init__(self, sys_argv):
        self.sys_argv = sys_argv
        self.verification()

    def verification(self):
        ‘‘‘由构造函数调用 验证用户输入的指令是否合法,如果不合法。打印帮助信息‘‘‘
        if len(self.sys_argv) < 2:  # sys.argv 列表中默认会带上文件名,所以长度必须不能小于2
            self.help_msg()
        cmd = self.sys_argv[1]
        if not hasattr(self, cmd):
            print(无效语法)
            self.help_msg()

    def help_msg(self):
        msg = ‘‘‘
        start       start FTP server
        stop        stop FTP server
        restart     restart FTP server
        createuser  username  create a ftp user
        其余功能还没扩展呢‘‘‘
        exit(msg)  # 如果命令输入的是错误的,就退出程序。因为 如果继续向后走的话,会因为没有这个命令报错。
        # 虽然可以,加上循环。让用户继续输入。不过 再来一次就好了。没必要那么麻烦

    def execute(self):
        ‘‘‘进行解析并执行指令,写在这里可以进行扩展‘‘‘
        cmd = self.sys_argv[1]
        func = getattr(self, cmd)
        func()   # 这里并没有传参数,因为sys.argv 是构造函数中的,相当于类中的一个全局变量。
        # 其余函数 直接调用就好了,不需要还要在这里传参数

    def start(self):
        ‘‘‘启动FTP server‘‘‘
        server = main.FTPServer(self)  # FTPServer 有可能用到当前这个程序的一些东西,那么就把实例本身
        server.run_forever()            # 当作一个参数 传给FTPServer  那就可以在FTPServer中使用这个类中的属性了

    def stop(self):
        ‘‘‘这是停止服务‘‘‘
        print(停止服务了)

    def restart(self):
        ‘‘‘重新启动服务器‘‘‘
        print(重新启动服务器)

    def createuser(self):
        ‘‘‘管理员创建用户使用的‘‘‘
        print(管理员专用)
management.py
技术分享图片
  1 import socket
  2 import json
  3 import hashlib
  4 import struct
  5 import os
  6 import subprocess
  7 from conf import settings
  8 from conf import config_tool
  9 
 10 
 11 class FTPServer(object):
 12     ‘‘‘处理与客户端所有的交互的 socket server
 13     所需要的参数,从settings 和 实例化时 传进来‘‘‘
 14     address_family = settings.ADDRESS_FAMILY
 15     socket_type = settings.SOCKET_TYPE
 16     allow_reuse_address = settings.ALLOW_REUSE_ADDRESS  # 是否重用端口开关 默认 Fales
 17     max_packet_size = settings.MAX_PACKET_SIZE  # 最大传输流量8192
 18     coding = settings.CODING  # utf-8
 19     request_queue_size = settings.MAX_SOCKET_LISTEN  # 最大挂起数量5
 20     server_dir = settings.USER_HOME_DIR  # 总家目录
 21 
 22     STATUS_CODE = {
 23         0: normal ,
 24         100: ls Error info,
 25         101: current dir has no file at all,
 26         200: Username Password Authentication Successful!,
 27         201: wrong username or password,
 28         300: The file was not find,
 29         301: find the file,
 30         302: The server did not receive the complete data,
 31         303: The server receive the complete data,
 32         400: The dirname was not find,
 33         401: Has entered this directory,
 34         500: "It‘s already on the top floor"
 35     }
 36 
 37     def __init__(self, management_instance, bind_and_acttivate=True):  # 在management类中start的位置,将management的实例传进来
 38         ‘‘‘构造函数,可扩展 不可覆盖‘‘‘
 39         self.management_instance = management_instance  # 类的构造函数中,接收这个参数,这样就能够使用到传进来的实例的对象中的属性
 40         self.socket = socket.socket(self.address_family, self.socket_type)
 41         self.config_obj = config_tool.load_all_user_info()  #
 42         if bind_and_acttivate:
 43             try:
 44                 self.server_bind()
 45                 self.server_activate()
 46             except Exception:
 47                 self.server_close()
 48 
 49     def server_bind(self):
 50         """由构造函数调用以绑定套接字"""
 51         if self.allow_reuse_address:
 52             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 53         self.socket.bind((settings.HOST, settings.PORT))
 54         self.server_address = self.socket.getsockname()
 55 
 56     def server_activate(self):
 57         """由构造函数调用以激活服务器 """
 58         self.socket.listen(self.request_queue_size)
 59 
 60     def get_request(self):
 61         """从套接字获取请求和客户机地址。 """
 62         return self.socket.accept()
 63 
 64     def server_close(self):
 65         """调用以清理服务器。"""
 66         self.socket.close()
 67 
 68     def close_request(self):
 69         """调用以清除单个请求"""
 70         self.request.close()
 71 
 72     def run_forever(self):
 73         ‘‘‘启动socket server 运行到天荒地老‘‘‘
 74         print(starting luffyFTP server on %s:%s.center(50, -) % (settings.HOST, settings.PORT))
 75         while True:
 76             try:
 77                 self.request, self.client_addr = self.get_request()
 78                 print(from client %s % (self.client_addr,))
 79                 # print(self.request, self.cline_addr)
 80                 self.handle()
 81             except Exception:
 82                 print(客户端 %s 断开连接 % (self.client_addr,))
 83                 self.close_request()
 84 
 85     def handle(self):
 86         ‘‘‘接收客户端发来的报头 解析之后 进行相应的操作‘‘‘
 87         while True:
 88             raw_data = self.request.recv(4)
 89             if not raw_data:
 90                 print(client %s disconnection % self.client_addr)
 91                 del self.request, self.client_addr
 92                 break
 93             data_len = struct.unpack(i, raw_data)[0]
 94             date_json = self.request.recv(data_len).decode(self.coding)
 95             data_dic = json.loads(date_json)
 96             action_type = data_dic.get(action_type)
 97             if action_type:  # 不能为空
 98                 if hasattr(self, "_%s" % action_type):
 99                     func = getattr(self, "_%s" % action_type)
100                     func(data_dic)
101 
102     def send_response(self, status_code, *args, **kwargs):
103         ‘‘‘打包发送状态码消息给客户端‘‘‘
104         data = kwargs
105         data[status_code] = status_code
106         data[status_msg] = self.STATUS_CODE[status_code]
107         bytes_data = json.dumps(data).encode(utf-8)
108         head_struct = struct.pack(i, len(bytes_data))
109 
110         self.request.send(head_struct)   # 这里时通信循环干的事情了,一定要是self.request
111         self.request.send(bytes_data)
112 
113     def authentication(self, username, password):
114         ‘‘‘对用户名密码进行验证‘‘‘
115         if username in self.config_obj:
116             _password = self.config_obj[username][password]
117             md5_obj = hashlib.md5()
118             md5_obj.update(password.encode(utf-8))
119             if md5_obj.hexdigest() == _password:
120                 return True
121 
122     def _auth(self, data):
123         ‘‘‘处理用户认证请求‘‘‘
124         if self.authentication(data.get(username), data.get(password)):
125             self.home_dir = os.path.join(self.server_dir, data.get(username))
126             self.userhome_dir = os.path.join(self.server_dir, data.get(username))
127             self.send_response(status_code=200,)
128         else:
129             self.send_response(status_code=201)
130 
131     def _put(self, data):
132         ‘‘‘用户进行上传,‘‘‘
133         file_path = os.path.normpath(os.path.join(self.userhome_dir, data[filename]))
134         filesize = data[filesize]
135         cilent_md5 = data[md5value]
136         result = self.recv_file(file_path, cilent_md5, filesize)
137         if result:
138             self.send_response(status_code=303)
139         else:
140             self.send_response(status_code=302)
141 
142     def _get(self, data):
143         ‘‘‘用户进行下载‘‘‘
144         file_path = os.path.normpath(os.path.join(self.userhome_dir, data[filename]))
145         if not os.path.isfile(file_path):
146             self.send_response(status_code=300)
147         else:
148             filesize = os.path.getsize(file_path)
149             md5value = config_tool.make_md5_on_file(file_path)
150             self.send_response(status_code=301, filesize=filesize, md5value=md5value, filename=data[filename])
151             self.send_file(file_path)
152 
153     def recv_file(self, file_path, cilent_md5, filesize, recv_size=0):
154         ‘‘‘接收文件,需要文件存放路径,MD5值,文件大小, 已接受的文件大小‘‘‘
155 
156         with open(file_path, ab) as f:
157             while recv_size < filesize:
158                 recv_data = self.request.recv(self.max_packet_size)
159                 if not recv_data: break
160                 f.write(recv_data)
161                 recv_size += len(recv_data)
162         md5value = config_tool.make_md5_on_file(file_path)
163         if md5value == cilent_md5:
164             return True
165 
166     def send_file(self, file_path, send_size=0):
167         with open(file_path, rb) as f:
168             f.seek(send_size)
169             for line in f:
170                 self.request.send(line)
171 
172     def _re_get(self, data):
173         abs_file = data[abs_file].strip(\\)
174         file_path = os.path.normpath(os.path.join(settings.USER_HOME_DIR, abs_file))
175         if not os.path.isfile(file_path):
176             self.send_response(status_code=300)
177         if os.path.getsize(file_path) != data[total_size]:
178             self.send_response(status_code=300)
179         else:
180             md5value = config_tool.make_md5_on_file(file_path)
181             self.send_response(status_code=301, md5value=md5value, filename=os.path.basename(file_path))
182             self.send_file(file_path, send_size=data[received_file_size])
183 
184     def _re_put(self, data):
185         ‘‘‘用于用户上传,没有传完整,续传使用‘‘‘
186         abs_file = data[abs_file].strip(\\)
187         file_path = os.path.normpath(os.path.join(settings.USER_HOME_DIR, abs_file))
188         md5value = data[md5value]
189 
190         if not os.path.isfile(file_path):
191             self.send_response(status_code=300)
192         else:
193             received_file_size = os.path.getsize(file_path)  # 返回给客户端,已经收到了多少字节
194             self.send_response(status_code=301, file_size=received_file_size)
195             filesize = data[filesize] - received_file_size
196             result = self.recv_file(file_path, md5value, filesize)
197             if result:
198                 self.send_response(status_code=303)
199             else:
200                 self.send_response(status_code=302)
201 
202     def _ls(self, data):
203         ‘‘‘给客户端显示当前文件下有哪些内容‘‘‘
204         cmd_boj = subprocess.Popen(dir %s % self.userhome_dir, shell=True, stdout=subprocess.PIPE,
205                          stderr=subprocess.PIPE)
206         stdout = cmd_boj.stdout.read()
207         stderr = cmd_boj.stderr.read()
208         cmd_result = (stdout + stderr).decode(GBK)
209         if not cmd_result:
210             self.send_response(status_code=101)
211         else:
212             self.send_response(status_code=0, dir_info=cmd_result)
213 
214     def _cd(self, data):
215         ‘‘‘进入用户想要进入的目录当中‘‘‘
216         dirname = data[dirname]
217         if not dirname.startswith(.):
218             if os.path.isdir(os.path.join(self.userhome_dir, dirname)):
219                 self.userhome_dir = os.path.join(self.userhome_dir, dirname)
220                 self.client_dir = self.userhome_dir.replace(settings.USER_HOME_DIR, ‘‘)
221                 self.send_response(status_code=401, dirn=self.client_dir)
222             else:
223                 self.send_response(status_code=400)
224         else:
225             li = dirname.split(\\)
226             count = 0
227             while count < len(li):
228                 ‘‘‘循环 如果 当前路径 已经到用户的家目录,就返回500 已经到达最顶层‘‘‘
229                 if self.userhome_dir == self.home_dir:
230                     self.send_response(status_code=500)
231                 else:
232                     self.userhome_dir = os.path.dirname(self.userhome_dir)
233                     self.client_dir = self.userhome_dir.replace(settings.USER_HOME_DIR, ‘‘)
234                     self.send_response(status_code=0, dirn=self.client_dir)
235                 count += 1
236 
237     def _make_dir(self):
238         ‘‘‘创建用户想要创建的文件夹‘‘‘
239         print(创建用户想要创建的文件夹)
main.py

 



以上是关于FTPserver的主要内容,如果未能解决你的问题,请参考以下文章

vsftpd简介和ftpserver在win10上的下载及安装过程

apache ftpserver 是主动还是被动

Windows 上搭建Apache FtpServer

(转载)Windows 上搭建Apache FtpServer

独立运行Apache FtpServer 1.0.0启动时报错

windows做FTPserver的一种方法