Python, socket編程, 上傳下載文件, 實現一個超簡化版ftp

基於Python3編寫, 實現文件上傳下載, 支持多個客戶端TCP方式連接服務端, 命令行操作

 

預覽圖:

 

download爲客戶端保存文件目錄, resources爲服務端保存文件目錄

 

代碼分爲服務端和客戶端兩部分, 代碼如下:

 

FileServer.py

#!/usr/bin/python3
# -*- coding:utf-8 -*-

import socket
import threading
import time
import os
import os.path
import json    #用於將列表或字典轉換成json字符串傳輸

first = r'./resources'
os.chdir(first)  # 把first設爲當前工作路徑
IP = ''
PORT = 50008
s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
s.bind( (IP, PORT) )
s.listen(3)

def tcp_connect(conn, addr):
    print('Connected by: ', addr)
    
    while True:
        data = conn.recv(1024)
        data = data.decode()
        print('Received message from {0}: {1}'.format(addr, data))
        if data == 'quit':
            print('Disconnected from {0}'.format(addr))
            break
        order = data.split()[0]  # 獲取動作
        recv_func(order, data)
            
    conn.close()

# 傳輸當前目錄列表
def sendList():
    listdir = os.listdir(os.getcwd())
    listdir = json.dumps(listdir)
    conn.sendall(listdir.encode())

# 發送文件函數
def sendFile(message):
    name = message.split()[1]  #獲取第二個參數(文件名)
    # 如果沒有指定下載的文件則給客戶端返回False
    if name in os.listdir() and '.' in name:
        conn.send('ok!'.encode())
        fileName = r'./' + name
        with open(fileName, 'rb') as f:    
            while True:
                a = f.read(1024)
                if not a:
                    break
                conn.send(a)
        time.sleep(0.1)  # 延時確保文件發送完整
        conn.send('EOF'.encode())
        print('文件發送完成!')
    else:
        conn.send('False'.encode())

# 保存上傳的文件到當前工作目錄
def recvFile(message):
    name = message.split()[1] #獲取文件名
    fileName = r'./' + name
    print('開始保存!')
    with open(fileName, 'wb') as f:
        while True:
            data = conn.recv(1024)
            if data == 'EOF'.encode():
                print('保存成功!')
                break
            f.write(data)

# 發送當前工作目錄
def pwd():
    path = os.getcwd()
    conn.send(path.encode())

# 切換工作目錄
def cd(message):
    message = message.split()[1]  # 截取目錄名
    # 如果目錄不存在或不是..則報錯
    if not message in os.listdir():
        if message != '..':
            conn.send('False2'.encode())
            return
    if '.' in message:
        if message != '..':
            conn.send('False1'.encode())
            return
    f = r'./' + message
    os.chdir(f)
    pwd()

# 判斷輸入的命令並執行對應的函數
def recv_func(order, message):
    if order == 'get':
        return sendFile(message)
    elif order == 'put':
        return recvFile(message)
    elif order == 'dir':
        return sendList()
    elif order == 'pwd':
        return pwd()
    elif order == 'cd':
        return cd(message)

print('tcp server is running...')
while True:
    conn, addr = s.accept()
    t = threading.Thread(target=tcp_connect, args=(conn, addr))
    t.start()
s.close()

 

FileClient.py

#!/usr/bin/python3
# -*- coding:utf-8 -*-

'''
    登錄名: ftp  密碼: 空
    服務端起始工作目錄爲resource文件夾
    get 文件名:下載文件到download文件夾, 如果文件已存在, 可選擇是否覆蓋
    put 文件名: 上傳客戶端所在的指定文件到服務端工作目錄下
    cd 目錄名 or ..: 進入或返回上一文件夾
    dir: 顯示當前目錄所有文件和目錄名
    help: 可用命令  quit: 斷開連接  cls: 清屏

'''

import socket
import threading
import time
import os
import json    #用於將列表或字典轉換成json字符串傳輸

IP = '127.0.0.1'
PORT = 50008
s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
s.connect((IP, PORT))

def main(): 
    user = input('user: ')
    pwd = input('password: ')
    if pwd == '' and user == 'ftp':
        while True:
            message = input('>>> ')
            if message == '':
                continue
            if message == 'quit':
                s.send(message.encode())
                break
            enter = message.split()[0]  # 獲取命令的第一個參數(動作)
            ent_func(enter, message)
            
    else:
        input('用戶名或密碼錯誤!')
        main()
    s.close()



# 輸入help或命令有誤時顯示(help)
def helpList():
    print('''
get: 下載\tput: 上傳\thelp: 可用命令
quit: 斷開連接\tcd: 進入文件夾\tdir: 當前目錄
cls: 清屏''')

# 接收下載文件(get)
def get(message):
    name = message.split()
    if len(name) != 2:
        return print('請輸入文件名') 
    name = name[1]  # 獲取命令的第二個參數(文件名)
    # 判斷是否已下載該文件
    if name in os.listdir(r'./download'):
        while True:
            ch = input('文件已存在是否覆蓋[Y/N]:')
            if ch == 'Y' or ch == 'y':
                break
            elif ch == 'N' or ch == 'n':
                print('取消下載')
                return
            else:
                continue
    # 判斷服務端是否有指定下載的文件, 沒有則報錯
    s.send(message.encode())
    data = s.recv(1024)
    if data.decode() == 'False':
        print('沒有該文件')
        return
    fileName = r'./download/' + name
    print('開始下載文件!')
    with open(fileName, 'wb') as f:
        while True:
            data = s.recv(1024)
            if data == 'EOF'.encode():
                print('下載完成!')
                break
            f.write(data)

# 上傳客戶端所在文件夾中指定的文件到服務端
def put(message):
    name = message.split()
    if len(name) != 2:
        return print('請輸入文件名')
    name = name[1]  # 獲取文件名
    # 判斷 Upload 目錄中是否有該文件
    if not name in os.listdir():
        print('沒有該文件')
        return
    if '.' not in name:
        print('請輸入正確的文件名!')
        return
    s.send(message.encode())
    fileName = r'./' + name
    print('開始上傳文件!')
    with open(fileName, 'rb') as f:
        while True:
            a = f.read(1024)
            if not a:
                break
            s.send(a)
        time.sleep(0.1)  # 延時確保文件發送完整
        s.send('EOF'.encode())
        print('文件上傳成功')

# 將接收到的目錄文件列表打印出來(dir)
def recvList(enter):
    s.send(enter.encode())
    data = s.recv(1024)
    data = json.loads(data.decode())
    for i in range(len(data)):
        print(data[i-1])

# 打印服務端當前工作目錄(pwd)
def pwd(enter):
    s.send(enter.encode())
    data = s.recv(1024)
    print(data.decode())

# 進入指定目錄(cd)
def cd(enter, message):
    s.send(message.encode())
    data = s.recv(1024).decode()
    if data == 'False1':
        print('請輸入正確的目錄名')
        return
    if data == 'False2':
        print('沒有該目錄!')
        return
    print(data)

# 清屏, 命令行下有效(cls)
def cls():
    os.system('cls')

# 判斷輸入的命令並執行對應的函數
def ent_func(enter, message):
    if enter == 'help':
        return helpList()
    elif enter == 'get':
        return get(message)
    elif enter == 'put':
        return put(message)
    elif enter == 'dir':
        return recvList(enter)
    elif enter == 'pwd':
        return pwd(enter)
    elif enter == 'cd':
        return cd(enter, message)
    elif enter == 'cls':
        return cls()
    else:
        print('無效命令')
       
main()

 

有錯誤或者疑問請指出謝謝!

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