Flask项目能打包为单个exe文件运行?知道原理后简直不要太简单!

简介: Python打包工具主要有三种:py2exe、PyInstaller和cx_Freeze。虽然是三种工具,但是实现打包的方式都是大同小异。无非将Python运行所需的基础dll文件和源码依赖的模块筛选后聚合在一起,从而达到脱离环境单独运行的目的。但其中比较新奇的是它们居然可以将最终代码打包成单个文件去运行,简直不要太神奇。清风常用的打包工具为pyinstaller,安装下载简单,网上的文档也很齐全。

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%就能确定了...

网络异常,图片无法展示
|

windwos临时目录


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为例说明。

首先看下代码结构:

网络异常,图片无法展示
|

Flask代码目录

settings中放了Flask的一些config配置,manage.py通过蓝图注册HttpServer中views下的account与home模块。那么,现在我们需要将代码中的static、templates、settings(测试发现这个配置文件也没办法自动打包,需要手动追加)成单个文件呢?pyinstaller提供了一个[--add-data <SRC;DEST or SRC:DEST>]的参数,整体打包命令如下:

pyinstaller -F -i BreezePython.ico --add-data="HttpServer\static;HttpServer\static" --add-data="HttpServer\templates;Httpserver\templates" --add-data="settings.py;." manage.py

原理就是保持代码中的路径一致,如果是当前路径使用.进行替换。

有些人觉得这个一个一个的添加太麻烦了,那么还有另一种思路。来看看我们打包后的目录:


网络异常,图片无法展示
|

spec文件的妙用


打包完成后,会生成一个main应用.spec的文件,通过我们刚才一顿--add-data的操作后,spec有什么区别么?


网络异常,图片无法展示
|

通过datas添加数据


所以我们可以换另一种方式加载依赖文件:

  1. 首次打包时直接-F 完成打包
  2. 编辑*.spec文件,通过在列表中添加对应元祖信息的方式,追加以来稳健
  3. pyinstaller -F *.spec进行二次打包即可追加文件至exe中。

来让我们看看打包后的exe是否可以执行吧:


网络异常,图片无法展示
|

打包效果展示


OK,一个exe文件拉起整个Flask项目,带着exe我们就可以脱离环境单独运行我们的HTTPServer了。是不是很炫酷?


临时文件监控复制


初次测试,可能存在打包路径错误的问题,每次去找临时路径查看太麻烦了,既然写代码,不如顺手写个动态监控_MEI路径并完成循环复制的功能,具体实现如下:

  1. 判断电脑的操作系统
  2. while循环监控临时目录
  3. 启动exe工具
  4. 获取exe创建的_MEI开头文件夹
  5. 将该临时文件夹拷贝到执行目录

最终代码实现如下:

# -*- 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,如果需要的朋友可以后台回复"服务"进行下载。

看到这里还不关注、点赞、转发下?听说这样三连操作,写代码没BUG哦!


The End




相关文章
|
6月前
|
缓存 前端开发 JavaScript
flask各种版本的项目,终端命令运行方式的实现
flask各种版本的项目,终端命令运行方式的实现
288 4
|
6月前
|
数据可视化 API 开发者
通俗易懂:一步步教你 Flask 项目自动生成 API 文档
Flasgger,作为一款强大的 Flask 扩展,自动从 Flask 应用中提取并生成 OpenAPI 规范文档,配备 SwaggerUI,为开发者提供了一条快捷通道,让 API 的文档编制和交互式测试变得简单易行。Flasgger 的设计原则是简化开发流程,通过与 Flask 框架的无缝整合,让开发者可以更专注于应用逻辑的构建。
|
1月前
|
Python
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
这篇博客文章是关于如何使用Flask框架上传特征值数据到服务器端,并将其保存为txt文件的教程。
31 0
Flask学习笔记(三):基于Flask框架上传特征值(相关数据)到服务器端并保存为txt文件
|
2月前
|
前端开发 Python
Flask原理解析
Flask原理解析
|
3月前
|
数据库连接 开发者 Python
从 Flask 迁移到 FastAPI,这简直是一场奇妙之旅!性能飙升,代码更优,快来感受这份惊喜!
【8月更文挑战第31天】作为一名开发者,从 Flask 迁移至 FastAPI 是一次充满挑战与收获的旅程。起初,Flask 的简洁性和灵活性吸引了我,但随着项目扩展,其性能局限性和较弱的类型提示支持逐渐显现。FastAPI 结合了 Flask 的简洁性和 Django 的强大功能,具备高性能、类型提示支持及自动文档生成等优势,更适合构建现代化 Web 应用。尽管迁移过程中需对现有代码进行调整,以适应 FastAPI 的类型提示和依赖注入机制,但最终应用性能和代码可维护性均显著提升,整个过程也大大提升了我的开发技能。
78 0
|
3月前
|
Linux Python
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
|
4月前
|
安全 前端开发 API
震惊!掌握Django/Flask后,我竟然轻松征服了所有Web项目难题!
【7月更文挑战第15天】Python Web开发中,Django以其全面功能见长,如ORM、模板引擎,助你驾驭复杂需求;Flask则以轻量灵活取胜,适合快速迭代。两者结合使用,无论是数据库操作、用户认证还是API开发,都能让你应对Web挑战游刃有余。掌握这两者,Web项目难题变得易如反掌!
76 10
|
4月前
|
JSON 数据格式 Python
Flask实现内部接口----pycharm安装及新建,location代表着文件路径,下面是Python的环境,Flask是由Python开发的框架,Python文件接口ython通过GET发送
Flask实现内部接口----pycharm安装及新建,location代表着文件路径,下面是Python的环境,Flask是由Python开发的框架,Python文件接口ython通过GET发送
|
4月前
|
文字识别 Java Python
文本,文识08图片保存()上,最方便在于整体生成代码,serivce及实体类,base64编码保存图片文件,调用flask实现内部ocr接口,通过paddleocr识别,解析结果,base64转图片
文本,文识08图片保存()上,最方便在于整体生成代码,serivce及实体类,base64编码保存图片文件,调用flask实现内部ocr接口,通过paddleocr识别,解析结果,base64转图片
|
5月前
|
数据处理 Python
Flask 项目工程目录层级划分
本文介绍了如何将 Flask 项目工程目录层级按照主题分类划分,主要包括模型层、视图层、表单层、模板文件和静态文件。通过合理地组织项目文件,可以提高项目的可读性、可维护性和可扩展性。
88 5