Flask項目能打包為單個exe文件運行?掌握原理后居然如此簡單!
python打包exe工具
Python打包工具主要有三種:py2exe、PyInstaller和cx_Freeze。雖然是三種工具,但是實現打包的方式都是大同小異。無非將Python運行所需的基礎dll文件和源碼依賴的模塊篩選后聚合在一起,從而達到脫離環境單獨運行的目的。但其中比較新奇的是它們居然可以將最終代碼打包成單個文件去運行,簡直不要太神奇。清風常用的打包工具為pyinstaller,安裝下載簡單,網上的文檔也很齊全。
打包的exe如何執行
但凡大家看到python打包exe工具的優勢,都會提到一句**打包成單個文件,可以保護源碼不外泄。**對于新手來說,這個理由完美,單個文件怎么操作感覺也不會獲得源碼,但我只能說,天真啊!pyinstaller的官方文檔:pyinstaller.readthedocs.io:How the One-File Program Works中有相關內容的詳細說明,為了方便,我簡單翻譯下:
引導加載程序也是單文件捆綁包的核心。啟動后,它將在此操作系統的相應臨時文件夾位置中創建一個臨時文件夾。該文件夾名為_MEIxxxxxx其中xxxxxx是一個隨機數。一個可執行文件包含腳本使用的所有Python模塊的嵌入式歸檔,以及任何非Python支持文件(例如,.so文件)的壓縮副本。引導加載程序解壓縮支持文件,并將副本寫入臨時文件夾。這可能需要一些時間。這就是為什么單文件應用程序比單文件夾應用程序啟動慢的原因。
所以單個的exe文件在執行時,會先在系統臨時目錄下創建一個_MEI開頭的文件夾,然后解壓源碼、依賴文件后,運行該臨時文件夾下的內容。其中windows的臨時文件夾通常為:
C:\Windows\Temp_MEIxxxx 或
C:\Users\用戶名\AppData\Local\Temp_MEIxxxx
但經過多次測試,幾乎全在后者的目錄下。其實只要在cmd下輸入echo %temp% 或者 %tmp%就能確定了...
Linux的臨時文件夾目錄自然是在: /tmp/_MEIxxxxx
當然,如果每次exe執行時,都會創建臨時文件夾,但執行完成后又不銷毀,豈不是早就導致我們的電腦磁盤空間溢出了。所以官方是有說明的:
創建臨時文件夾后,引導程序將在臨時文件夾的上下文中繼續與單文件夾捆綁軟件一樣進行。當捆綁的代碼終止時,引導加載程序將刪除臨時文件夾。如果程序崩潰或被殺死,則不會刪除該文件夾(在Unix上為kill -9,在Windows上為Task Manager殺死,在Mac OS上為“ Force Quit”)。因此,如果您的應用程序頻繁崩潰,則用戶將丟失磁盤空間到多個臨時文件夾。_MEIxxxxxx_MEIxxxxxx
打包Flask項目
pyinstalelr的基礎使用就不在這里過多介紹了,之前的文章有過詳細的說明:Python打包工具--Pyinstaller詳細介紹:https://mp.weixin.qq.com/s/smsO0n8M18J7ofoOsWEjoQ
我們只需要知道pyinstaller -F(onefile)參數即可將代碼最終打包為單個的exe文件即可。但是Flask的static、templates該怎么打包呢?讓我們以之前開發過的一個FlaskHttpserver為例說明。首先看下代碼結構:
settings中放了Flask的一些config配置,manage.py通過藍圖注冊HttpServer中views下的account與home模塊。那么,現在我們需要將代碼中的static、templates、settings(測試發現這個配置文件也沒辦法自動打包,需要手動追加)成單個文件呢?pyinstaller提供了一個
[--add-data
的參數,整體打包命令如下:
pyinstaller?-F?-i?BreezePython.ico?--add-data="HttpServer\static;HttpServer\static"?--add-data="HttpServer\templates;Httpserver\templates"?--add-data="settings.py;."?manage.py
原理就是保持代碼中的路徑一致,如果是當前路徑使用.進行替換。有些人覺得這個一個一個的添加太麻煩了,那么還有另一種思路。來看看我們打包后的目錄:
打包完成后,會生成一個main應用.spec的文件,通過我們剛才一頓--add-data的操作后,spec有什么區別么?
所以我們可以換另一種方式加載依賴文件:
首次打包時直接-F 完成打包
編輯*.spec文件,通過在列表中添加對應元祖信息的方式,追加以來穩健
pyinstaller -F *.spec進行二次打包即可追加文件至exe中。
來讓我們看看打包后的exe是否可以執行吧:
OK,一個exe文件拉起整個Flask項目,帶著exe我們就可以脫離環境單獨運行我們的HTTPServer了。是不是很炫酷?
臨時文件監控復制
初次測試,可能存在打包路徑錯誤的問題,每次去找臨時路徑查看太麻煩了,既然寫代碼,不如順手寫個動態監控_MEI路徑并完成循環復制的功能,具體實現如下:
判斷電腦的操作系統
while循環監控臨時目錄
啟動exe工具
獲取exe創建的_MEI開頭文件夾
將該臨時文件夾拷貝到執行目錄
最終代碼實現如下:
#?-*-?coding:?utf-8?-*- #?@Author???:?王翔 #?@微信號???:?King_Uranus #?@公眾號????:?清風Python #?@GitHub???:?https://github.com/BreezePython #?@Date?????:?2020/11/17?23:50:09 #?@Software?:?PyCharm #?@version??:Python?3.7.3 #?@File?????:?get_source_code.py import?platform import?os import?time import?shutil def?get_tmp_path(): ????if?platform.platform().lower().startswith('windows'): ????????return?os.getenv('temp') ????else: ????????return?'/tmp' class?GetSourceCode: ????def?__init__(self): ????????self.base_path?=?os.path.dirname(__file__) ????????self.tmp_path?=?get_tmp_path() ????????self.basic_dirs?=?self.get_dirs() ????????self.code_dir?=?None ????def?get_dirs(self): ????????for?root,?dirs,?files?in?os.walk(self.tmp_path): ????????????return?set(dirs) ????def?get_source_dir(self): ????????while?True: ????????????_dir?=?list(self.get_dirs()?-?self.basic_dirs) ????????????if?_dir?and?_dir[0].startswith('_MEI'): ????????????????self.code_dir?=?_dir[0] ????????????????print("find?source?code?dir?%s"?%?self.code_dir) ????????????????break ????????????else: ????????????????time.sleep(0.2) ????????self.copy_code_dir() ????def?copy_code_dir(self): ????????abs_tmp_path?=?os.path.join(self.tmp_path,?self.code_dir) ????????while?os.path.exists(abs_tmp_path): ????????????source_path?=?os.path.join(self.base_path,?self.code_dir) ????????????if?not?os.path.exists(source_path): ????????????????os.mkdir(source_path) ????????????for?root,?dirs,?files?in?os.walk(abs_tmp_path): ????????????????for?file?in?files: ????????????????????remote_path?=?root.replace(abs_tmp_path,?source_path).replace('\\',?'/') ????????????????????if?not?os.path.exists(remote_path): ????????????????????????print(remote_path) ????????????????????????os.makedirs(remote_path) ????????????????????if?not?os.path.exists(remote_path?+?'/'?+?file): ????????????????????????shutil.copy(os.path.join(root,?file),?remote_path) ????????print("Get?source?code?end.") if?__name__?==?'__main__': ????print("start?Source?Code?Analyse?project.") ????print("Monitoring?source?files...") ????g?=?GetSourceCode() ????g.get_source_dir()
看看效果如何:
ok,快把你積攢已久的代碼篩選下,看看那些適合打包成exe,拿去給朋友們炫耀吧!關于FlaskHttpserver,如果需要的朋友可以后臺回復 服務 進行下載。
-------
本文轉自:清風Python公眾號
Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。