python實現ftp服務器(使用wxpython實現GUI界面)

開發背景:

有時須要在局域網傳輸文件,老是要用U盤傳輸,要是多我的都須要一樣的文件,U盤就有點麻煩了,Windows上的文件共享配置步驟不多,可是常常由於各類緣由失敗,又要檢查來檢查去的。因而考慮到經過FTP協議來傳輸文件,可是出名的FTP服務器軟件相似Serv-U 這種,功能是很強大,可是配置也不少,我只是臨時傳輸下文件,但願能夠免安裝,一鍵啓動FTP,一鍵關閉FTP。因而就想使用python實現FTP服務器,再打包成exe文件。python

開發環境:

win 7 64位,Python 3.6.2,pyftpdlib,wxpython,pyinstaller 3.3.1服務器

具體思路:

經過pyftpdlib庫實現FTP 功能ftpserver.py,使用wxpython實現GUI界面wxgui.py,在wxgui.py上生成配置文件config.ini,ftpserver.py再獲取config.ini中的信息啓動FTP,最後使用pyinstaller打包成exe可執行文件。多線程

軟件截圖:

不過剛開發好的軟件,應該有挺多BUG的,之後使用過程當中再慢慢改進。
打開軟件:
python實現ftp服務器(使用wxpython實現GUI界面)
選擇FTP目錄:
python實現ftp服務器(使用wxpython實現GUI界面)
啓動FTP:
python實現ftp服務器(使用wxpython實現GUI界面)app

關鍵步驟:

此次小項目中使用到了ini格式的配置文件,該配置文件提供給FTP服務啓動須要的配置信息(是否匿名,FTP端口等),還能夠記錄FTP啓動時GUI界面狀態,使界面關閉後打開仍是FTP啓動時的界面。
ini 文件是文本文件,中間的數據格式通常爲: socket

[Section1 Name] 
KeyName1=value1 
KeyName2=value2 
...

python能夠經過configparse內置模塊寫入讀取配置信息
python實現ftp服務器(使用wxpython實現GUI界面)
該界面啓動就執行下面的Python語句:ide

import configparse
config=configparser.ConfigParser()
config.add_section('ftpd')#添加一個新的section
config.set('ftpd','anonymous',str(not check.GetValue()))#判斷複選框是否選中
config.set('ftpd','user',usertext.GetValue())#獲取用戶名
config.set('ftpd','password',passtext.GetValue())#獲取密碼
config.set('ftpd','port',porttext.GetValue())#獲取端口
config.set('ftpd','dir',dirtext.GetValue())#獲取目錄
#寫入到config.ini配置
with open('config.ini','w') as conf:
    config.write(conf)

寫入的配置文件config.ini以下:函數

[ftpd]
anonymous = False
user = aaa
password = aaa
port = 21
dir = E:\

接着就是ftpserver.py如何獲取config.ini配置文件的信息了oop

config = configparser.ConfigParser()
config.read('config.ini')
config.get('ftpd',user)#獲取用戶名
config.get('ftpd',password)#獲取密碼
...

wxgui.py也能夠經過config.ini配置文件來恢復程序界面關閉前的模樣ui

def getconfig(v):#pyserver.py中的函數
    #獲取config.ini配置文件的信息
    config = configparser.ConfigParser()
    config.read('config.ini')
    return config.get('ftpd',v)
if os.path.isfile('config.ini'):#config.ini文件存在的話,就獲取配置文件信息。
    if ftpserver.getconfig('anonymous')=='False':
        check.SetValue(1)#設置複選框爲選中狀態
        usertext.SetEditable(1)#容許編輯
        passtext.SetEditable(1)
    usertext.SetValue(ftpserver.getconfig('user'))#設置用戶名輸入框的值
    passtext.SetValue(ftpserver.getconfig('password'))
    porttext.SetValue(ftpserver.getconfig('port'))
    dirtext.SetValue(ftpserver.getconfig('dir'))

具體源代碼:

實現FTP功能代碼ftpserver.py:url

# coding:utf-8
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
import logging
import configparser
import os
def ftpd():
    authorizer = DummyAuthorizer()
    if getconfig('anonymous')=='True':
        #添加匿名用戶
        authorizer.add_anonymous(getconfig('dir'),perm='elradfmwM')
    else:    
        #添加用戶,須要帳號和密碼登陸
        authorizer.add_user(getconfig('user'), getconfig('password'), getconfig('dir'), perm='elradfmwM')
    handler = FTPHandler#初始化處理客戶端命令的類
    handler.authorizer = authorizer#選擇登陸方式(是否匿名)
    logging.basicConfig(filename='ftpd.log', level=logging.INFO) #日誌信息寫入ftpd.log
    address = ('0.0.0.0', getconfig('port'))#設置服務器的監聽地址和端口
    server = FTPServer(address, handler)
    server.max_cons = 256                              #給連接設置限制
    server.max_cons_per_ip = 5
    server.serve_forever()                             # 啓動FTP

def getconfig(v):
    #獲取config.ini配置文件的信息
    config = configparser.ConfigParser()
    config.read('config.ini')
    return config.get('ftpd',v)
if __name__ == '__main__':
    ftpd()

實現GUI界面代碼wxgui.py:

# -*- coding:utf-8 -*-
import wx
import os
import sys
import configparser
import ftpserver
import time
import threading
import socket
def onclick(event): #選擇FTP目錄
    dlg=wx.DirDialog(window,'選擇共享目錄')
    if dlg.ShowModal() == wx.ID_OK:
        dirtext.SetValue(dlg.GetPath())

def onclose(event):  #退出事件
    if startbutton.GetLabel()=='關閉FTP':
        dlg1 = wx.MessageDialog(None, "FTP正在運行,確認退出嗎?", "退出", wx.YES_NO | wx.ICON_EXCLAMATION)
        if dlg1.ShowModal()==wx.ID_YES:
            sys.exit()
    else:
        sys.exit()

def startftp(event): #點擊 啓動FTP 按鈕事件
    global t
    if startbutton.GetLabel()=='啓動FTP':
        startbutton.SetLabel('關閉FTP')
        #把FTP啓動信息寫入config.ini配置文件中
        config=configparser.ConfigParser()
        config.add_section('ftpd')
        config.set('ftpd','anonymous',str(not check.GetValue()))
        config.set('ftpd','user',usertext.GetValue())
        config.set('ftpd','password',passtext.GetValue())
        config.set('ftpd','port',porttext.GetValue())
        config.set('ftpd','dir',dirtext.GetValue())
        with open('config.ini','w') as conf:
            config.write(conf)
        time.sleep(1)
        #建立線程啓動FTP
        t=threading.Thread(target=ftpserver.ftpd)
        t.setDaemon(True)        
        t.start()
        iplist=socket.gethostbyname_ex(socket.gethostname())[2]
        ftpurl=''
        if iplist :
            for ip in iplist:
                ftpurl+='FTP地址:ftp://'+ip+':'+porttext.GetValue()+'\n'
        urllabel.SetLabel(ftpurl)
    else:
        dlg1 = wx.MessageDialog(None, "FTP正在運行,確認退出嗎?", "退出", wx.YES_NO | wx.ICON_EXCLAMATION)
        if dlg1.ShowModal()==wx.ID_YES:
            sys.exit()
def onchecked(event): #複選框事件
    usertext.SetEditable(event.IsChecked())
    passtext.SetEditable(event.IsChecked())

app = wx.App()#實例化wx.App
#建立頂級窗口,而且窗口不可改變尺寸
window = wx.Frame(None,title = "FTP服務器", size = (390, 260), pos=(400,300),style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX)
#綁定退出事件
window.Bind(wx.EVT_CLOSE,onclose)
#建立panel
panel = wx.Panel(window)
font = wx.Font(11, wx.SWISS, wx.NORMAL, wx.NORMAL)
#建立複選框,並綁定事件
check=wx.CheckBox(panel,-1,'使用帳戶密碼登陸',pos=(20,20),size=(200,-1))
check.SetFont(font)
window.Bind(wx.EVT_CHECKBOX,onchecked)
#建立靜態文本和文本輸入框
userlabel = wx.StaticText(panel, label = "用戶名:", pos=(35,45))
userlabel.SetFont(font)
usertext = wx.TextCtrl(panel,-1,'',size=(95,20),pos=(90,42),style = wx.TE_READONLY) 
passlabel = wx.StaticText(panel, label = "密碼:",pos=(195,45))
passlabel.SetFont(font)
passtext = wx.TextCtrl(panel,-1,'',size=(95,20),pos=(235,42),style = wx.TE_PASSWORD)
passtext.SetEditable(False)
dirlabel = wx.StaticText(panel, label = "FTP目錄:",pos=(23,72))
dirlabel.SetFont(font)
dirtext = wx.TextCtrl(panel,-1,os.getcwd(),size=(215,20),pos=(88,70),style = wx.TE_READONLY)
#建立按鈕,而且綁定按鈕事件
button=wx.Button(panel,-1,'更改',pos=(310,70),size=(40,20))
button.SetFont(wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL))
window.Bind(wx.EVT_BUTTON,onclick,button)
portlabel = wx.StaticText(panel, label = "FTP端口:",pos=(23,104))
portlabel.SetFont(font)
porttext = wx.TextCtrl(panel,-1,'21',size=(51,20),pos=(88,102))
startbutton=wx.Button(panel,-1,'啓動FTP',pos=(160,130),size=(70,30))
startbutton.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL))
window.Bind(wx.EVT_BUTTON,startftp,startbutton)
#加載配置文件,使程序恢復到退出前的界面
if os.path.isfile('config.ini'):
    if ftpserver.getconfig('anonymous')=='False':
        check.SetValue(1)
        usertext.SetEditable(1)
        passtext.SetEditable(1)
    usertext.SetValue(ftpserver.getconfig('user'))
    passtext.SetValue(ftpserver.getconfig('password'))
    porttext.SetValue(ftpserver.getconfig('port'))
    dirtext.SetValue(ftpserver.getconfig('dir'))
urllabel = wx.StaticText(panel, label = "", pos=(80, 170))
urllabel.SetFont(font)
window.Show(True)#窗口可見
app.MainLoop() #主循環,處理事件

使用pyinstaller打包

若是順利的話,兩條命令就能夠成功打包了,反正我是沒那麼順利的了
pip安裝pyinstaller
pip install pyinstaller
開始打包:
pyinstaller -F wxgui.py -w
-F 參數:是生成單個可執行文件
-w參數:由於個人是GUI程序,因此我使用這參數去掉命令行窗口
生成的文件:
python實現ftp服務器(使用wxpython實現GUI界面)

問題總結:

此次開發這個小項目中遇到了個問題,百度谷歌都找不到解決的辦法。程序建立個線程去啓動FTP服務,當我想要中止FTP服務時,在不退出程序的狀況下該如何去中止FTP服務?問題已解決,把多線程換成多進程就能夠了,多進程自帶結束進程的方法

相關文章
相關標籤/搜索