標籤:python wxpython pyftpdlib FTP伺服器 pyinstaller
開發背景:
有時需要在區域網路傳輸檔案,總是要用隨身碟傳輸,要是多個人都需要同樣的檔案,隨身碟就有點麻煩了,Windows上的檔案分享權限設定配置步驟很少,但是經常因為各種原因失敗,又要檢查來檢查去的。於是考慮到通過FTP協議來傳輸檔案,但是出名的FTP伺服器軟體類似Serv-U 這種,功能是很強大,但是配置也很多,我只是臨時傳輸下檔案,希望可以免安裝,一鍵啟動FTP,一鍵關閉FTP。於是就想使用python實現FTP伺服器,再打包成exe檔案。
開發環境:
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的,以後使用過程中再慢慢改進。
開啟軟體:
選擇FTP目錄:
啟動FTP:
具體原始碼:
實現FTP功能代碼ftpserver.py:
# coding:utf-8from pyftpdlib.authorizers import DummyAuthorizerfrom pyftpdlib.handlers import FTPHandlerfrom pyftpdlib.servers import FTPServerimport loggingimport configparserimport osdef 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() # 啟動FTPdef 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 wximport osimport sysimport configparserimport ftpserverimport timeimport threadingimport socketdef 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)#建立panelpanel = 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程式,所以我使用這參數去掉命令列視窗
產生的檔案:
問題總結:
這次開發這個小項目中遇到了個問題,百度Google都找不到解決的辦法。
程式建立個線程去啟動FTP服務,當我想要停止FTP服務時,在不退出程式的情況下該如何去停止FTP服務?
python實現ftp伺服器(使用wxpython實現GUI介面)