Python FTP檔案傳輸

來源:互聯網
上載者:User

標籤:object   dial   txt   gre   type   status   exit   join   內容   

 

FTP Server
import socketimport structfrom concurrent.futures import ThreadPoolExecutorimport jsonimport hashlibimport osimport timefrom demo import common_utilsPUT_FILE_DIR = r‘C:\x\LuffyFTP\sharefile\server\put‘GET_FILE_DIR = r‘C:\x\LuffyFTP\sharefile\server\get‘IP_PORT = (‘127.0.0.1‘, 9999)def run_forever():    """     啟動socket     :return:     """    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    server_socket.bind(IP_PORT)    server_socket.listen(5)    print(‘Server Start,IP:%s, LISTENING PORT: %s.‘ %          IP_PORT)    pool = ThreadPoolExecutor(10)    while True:        conn, client_addr = server_socket.accept()        print(‘建立一個新的線程,和用戶端{}通訊‘.format(client_addr))        pool.submit(take_over_connection, conn, client_addr)def take_over_connection(conn, client_addr):    """    用來接管socket連結,每個線程接管一個連結    :param conn:    :param client_address:    :return:    """    print(‘MyServer‘)    server = MyServer(conn, client_addr)    server.handle_cmd()class MyServer(object):    """        處理用戶端所有的互動socket server        """    STATUS = {        300: ‘File not exist !‘,        301: ‘File  exist , and the msg include the file size!‘,        302: ‘File not exist !!!‘    }    def __init__(self, conn, client_addr):        self.conn = conn        self.client_addr = client_addr    def handle_cmd(self):        """               處理使用者命令互動               :return:               """        print(‘handle_cmd‘)        while True:            try:                # 收到前序長度                recv_pack = self.conn.recv(4)                if not recv_pack:                    print(                        ‘connect {} is lost ……‘.format(                            self.client_addr))                    break                # 解析前序                recv_length = struct.unpack(‘i‘, recv_pack)[0]                header_data = self.conn.recv(recv_length)                # json_data                json_data = json.loads(header_data.decode(‘utf-8‘))                print(‘recv data >>> {}‘.format(json_data))                action_type = json_data.get(‘action_type‘)                if action_type:                    # 使用反射                    if hasattr(self, ‘_{}‘.format(action_type)):                        func = getattr(self, ‘_{}‘.format(action_type))                        func(json_data)                else:                    print(‘invalid command‘)            except ConnectionResetError:  # 適用於windows作業系統                break    def send_response(self, status_code, **kwargs):        """                 向用戶端發送響應嗎                :param status:                :return:                """        # 構造訊息頭        message = {            ‘status‘: status_code,            ‘status_message‘: self.STATUS.get(status_code)        }        message.update(kwargs)  # 更新訊息        message_json = json.dumps(message)        # 為防止粘包,封裝訊息包        header_byte = message_json.encode(‘utf-8‘)        # 先發送前序的長度        self.conn.send(struct.pack(‘i‘, len(message_json)))        print(‘發送response前序的長度: {}‘.format(len(message_json)))        print(‘發送response前序內容:{}‘.format(message))        # 發送前序        self.conn.send(header_byte)    def _get(self, data):        """        下載檔案,如果檔案存在,發送狀態代碼+檔案大小+md5,傳送檔案        不存在,發送狀態代碼        :param data:        :return:        """        print(‘_get {}‘.format(data))        file_path = os.path.join(            GET_FILE_DIR,            data.get(‘file_name‘))        if os.path.isfile(file_path):            file_size = os.path.getsize(file_path)            print(                ‘file_path: {} file_size: {} ‘.format(                    file_path, file_size))            self.send_response(301, file_size=file_size, md5=common_utils.get_md5(                file_path), server_file_dir=os.path.dirname(file_path))            print(‘read to send file >>>‘, data.get(‘file_name‘))            with open(file_path, ‘rb‘) as f:                for line in f:                    self.conn.send(line)                else:                    print(‘send file {} done‘.format(file_path))        else:            self.send_response(302)    def _put(self, data):        """         拿到檔案名稱和大小,檢測本地是否存在相同檔案         如果存在,建立新檔案local_file_name+timestamp         如果存在,建立新檔案local_file_name        :param data:        :return:        """        print(‘_put {}‘.format(data))        file_size = data.get(‘file_size‘)        file_name = data.get(‘file_name‘)        file_path = os.path.join(            PUT_FILE_DIR,            file_name)        client_md5 = data.get(‘md5‘)        if os.path.isfile(file_path):            print(‘file is exist‘)            file_path = ‘{}.{}‘.format(file_path, str(int(time.time())))        tmp_file = ‘{}.down‘.format(file_path)        print(‘tmp_file:‘, tmp_file)        f = open(tmp_file, ‘wb‘)        recv_size = 0        print(‘put file {} start >>> ‘.format(file_path))        while recv_size < file_size:            data = self.conn.recv(8192)  # 接收檔案內容            f.write(data)            recv_size += len(data)        else:            print("\n")            print(                ‘-- file [{}] put done, received size [{}]‘.format(file_name, common_utils.bytes2human(                    os.path.getsize(tmp_file))))        f.close()        os.rename(tmp_file, file_path)        server_md5 = common_utils.get_md5(file_path)        if server_md5 == client_md5:            print(‘檔案上傳完整與用戶端一致‘)if __name__ == ‘__main__‘:    run_forever()

  

FTP Client
import argparseimport socketimport jsonimport structimport sysimport osBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))sys.path.append(BASE_DIR)from demo import common_utilsPUT_FILE_PATH = r‘C:\x\LuffyFTP\sharefile\client\put‘GET_FILE_PATH = r‘C:\x\LuffyFTP\sharefile\client\get‘IP_PORT = (‘127.0.0.1‘, 9999)class FtpClient():    """    ftp用戶端    """    def __init__(self):        self.client_sock = None        self.make_connect()    def make_connect(self):        """        串連伺服器        :return:        """        try:            self.client_sock = socket.socket(                socket.AF_INET, socket.SOCK_STREAM)            print(‘串連伺服器‘)            self.client_sock.connect(IP_PORT)  # 串連伺服器        except Exception as e:            print(‘串連伺服器異常‘, e)    def interactive(self):        """        互動        :return:        """        menu = """        1. 下載檔案 get 1.txt        2. 上傳檔案 put 1.txt        3. 退出 bye        """        print(menu)        while True:            user_input = input(‘請輸入 >>> ‘).strip()            if not user_input:                continue            cmd_list = user_input.split()            if hasattr(self, ‘_{}‘.format(cmd_list[0])):                func = getattr(self, ‘_{}‘.format(cmd_list[0]))                func(cmd_list)  # get    def send_msg(self, action_type, **kwargs):        """         打包訊息,發送到伺服器        :param action_type:        :param kwargs:        :return:        """        cmd = {            ‘action_type‘: action_type,        }        cmd.update(kwargs)  # 更新字典        cmd_json = json.dumps(cmd)        # 為防止粘包,封裝包        header_byte = cmd_json.encode(‘utf-8‘)        # 先發送前序的長度        self.client_sock.send(struct.pack(‘i‘, len(cmd_json)))        print(‘發送auth前序的長度: {}‘.format(len(cmd_json)))        print(‘發送auth前序內容:{}‘.format(cmd_json))        # 發送前序        self.client_sock.send(header_byte)    def arg_check(self, cmd_args, len_args):        if len(cmd_args) != len_args:            print(                ‘must provide {} parameters but received {}‘.format(len_args,                                                                    len(cmd_args)))            return False        else:            return True    def get_response(self):        """                收到伺服器向用戶端發送的響應                :return:                """        # 收到前序長度        recv_pack = self.client_sock.recv(4)        if recv_pack:            # 解析前序            recv_length = struct.unpack(‘i‘, recv_pack)[0]            header_data = self.client_sock.recv(recv_length)            # json_data            json_data = json.loads(header_data.decode(‘utf-8‘))            print(‘recv response >>> {}‘.format(json_data))            return json_data        else:            print(‘recv_pack is null !!!‘)            return None    def _get(self, cmd_args):        """        得到檔案,發送到遠程,等待返回訊息,        等待檔案,迴圈收檔案        :param cmd_args:        :return:        """        if self.arg_check(cmd_args, 2):            file_name = cmd_args[1]  # get filename            self.send_msg(‘get‘, file_name=file_name)            response_data = self.get_response()            if response_data.get(‘status‘) == 301:                file_size = response_data.get(‘file_size‘)                server_md5 = response_data.get(‘md5‘)                file_path = os.path.join(                    GET_FILE_PATH, file_name)                recv_size = 0                p = self.progress_bar(file_size)  # 進度條                p.send(None)                print(‘get file {} start >>> ‘.format(file_name))                tmp_file = ‘{}.down‘.format(file_path)                with open(tmp_file, ‘wb‘) as f:  # 寫下載檔案                    # 序列化儲存資料                    while recv_size < file_size:                        data = self.client_sock.recv(8192)                        f.write(data)                        recv_size += len(data)                        p.send(recv_size)                    else:                        print("\n")                        print(                            ‘-- file [{}] recv done, received size [{}]‘.format(file_name, file_size))                if os.path.isfile(file_path):  # 如果檔案存在,刪除後覆蓋檔案                    os.remove(file_path)                os.rename(tmp_file, file_path)                client_md5 = common_utils.get_md5(file_path)                if server_md5 == client_md5:                    print(‘檔案下載完整與服務端一致‘)            else:                print(response_data.get(‘status_message‘))    def _put(self, cmd_args):        """        1.上傳本地檔案到伺服器        2.確保本地檔案存在        3.把檔案名稱和檔案大小發送到遠程        4.傳送檔案內容        :return:        """        if self.arg_check(cmd_args, 2):            local_file_name = cmd_args[1]  # put filename            full_path = os.path.join(PUT_FILE_PATH, local_file_name)            if os.path.isfile(full_path):                total_size = os.path.getsize(full_path)                self.send_msg(                    ‘put‘,                    file_name=local_file_name,                    file_size=total_size, md5=common_utils.get_md5(full_path))                p = self.progress_bar(total_size)                p.send(None)                upload_size = 0                with open(full_path, ‘rb‘) as f:  # 傳送檔案                    for line in f:                        self.client_sock.send(line)                        upload_size += len(line)                        p.send(upload_size)                    else:                        print("\n")                        print(‘file upload done‘.center(50, ‘-‘))            else:                print(                    ‘file [{}] is not exist !!!‘.format(local_file_name))    def _bye(self, cmd_args):        """         退出        :return:        """        print("bye")        self.client_sock.close()        exit(0)    @staticmethod    def progress_bar(total_size):        """        顯示進度條        :param total_size:        :return:        """        current_percent = 0        last_percent = 0        while True:            recv_size = yield current_percent            current_percent = int(recv_size / total_size * 100)            print("#" * int(current_percent / 4) + ‘{percent}%‘.format(percent=int(current_percent)), end="\r",                  flush=True)if __name__ == ‘__main__‘:    c = FtpClient()    c.interactive()

 

common_util
import loggingfrom logging import handlersimport osfrom tkinter import Tk, filedialogimport osimport hashlibdef bytes2human(n):    # 檔案大小位元組單位轉換    symbols = (‘K‘, ‘M‘, ‘G‘, ‘T‘, ‘P‘, ‘E‘)    prefix = {}    for i, s in enumerate(symbols):        # << 左移” 左移一位表示乘2 即1 << 1=2,二位就表示4 即1 << 2=4,        # 10位就表示1024 即1 << 10=1024 就是2的n次方        prefix[s] = 1 << (i + 1) * 10    for s in reversed(symbols):        if n >= prefix[s]:            value = float(n) / prefix[s]            return ‘%.2f%s‘ % (value, s)    return "%sB" % ndef get_md5(file_path):    """    得到檔案MD5    :param file_path:    :return:    """    if os.path.isfile(file_path):        file_size = os.stat(file_path).st_size        md5_obj = hashlib.md5()  # hashlib        f = open(file_path, ‘rb‘)  # 開啟檔案        read_size = 0        while read_size < file_size:            read_byte = f.read(8192)            md5_obj.update(read_byte)  # update md5            read_size += len(read_byte)        hash_code = md5_obj.hexdigest()  # get md5 hexdigest        f.close()        print(‘file: [{}] \nsize: [{}] \nmd5: [{}]‘.format(            file_path, bytes2human(read_size), hash_code))        return str(hash_code)def get_dir_size_count(dir):    """    獲得檔案夾中所有檔案大小和檔案個數    :param dir:    :return:    """    size = 0    count = 0    for root, dirs, files in os.walk(dir):        size_li = [os.path.getsize(os.path.join(root, name))                   for name in files]        size += sum(size_li)        count += len(size_li)    print(‘目錄{}  檔案個數{}, 總共大小約{}‘.format(dir, count, bytes2human(size)))    return count, sizedef brows_local_filename(title=‘choose a file‘, force=False):    """    Select a local file by filedialog of tkinter.  Return an exist file path.    :param title:    :param force: If force is True user must choose a file.    :return:    """    tk = Tk()    tk.withdraw()    tk.wm_attributes(‘-topmost‘, 1)    while True:        filename = filedialog.askopenfilename(title=title)        if not force or filename:            break    tk.destroy()    return filenamedef brows_save_filename(title=‘save as‘):    """    Select a local path to save a file by filedialog of tkinter.Return a path for saving file.    :param title:    :return:    """    tk = Tk()    tk.withdraw()    tk.wm_attributes(‘-topmost‘, 1)    filename = filedialog.asksaveasfilename(title=title)    tk.destroy()    return filename

 

Python FTP檔案傳輸

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.