0
点赞
收藏
分享

微信扫一扫

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

南柯Taylor 2021-09-26 阅读 55
Python当歌

python打包exe工具

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

打包的exe如何执行

但凡大家看到python打包exe工具的优势,都会提到一句打包成单个文件,可以保护源码不外泄。对于新手来说,这个理由完美,单个文件怎么操作感觉也不会获得源码,但我只能说,天真啊!
pyinstaller的官方文档:
pyinstaller.readthedocs.io:How the One-File Program Works
中有相关内容的详细说明,为了方便,我简单翻译下:

所以单个的exe文件在执行时,会先在系统临时目录下创建一个_MEI开头的文件夹,然后解压源码、依赖文件后,运行该临时文件夹下的内容。
其中windows的临时文件夹通常为:
C:\Windows\Temp_MEIxxxx 或
C:\Users\用户名\AppData\Local\Temp_MEIxxxx

但经过多次测试,几乎全在后者的目录下。其实只要在cmd下输入echo %temp% 或者 %tmp%就能确定了...


Linux的临时文件夹目录自然是在:
**/tmp/_MEIxxxxx **

当然,如果每次exe执行时,都会创建临时文件夹,但执行完成后又不销毁,岂不是早就导致我们的电脑磁盘空间溢出了。所以官方是有说明的:

打包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 <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

原理就是保持代码中的路径一致,如果是当前路径使用.进行替换。
有些人觉得这个一个一个的添加太麻烦了,那么还有另一种思路。来看看我们打包后的目录:



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



所以我们可以换另一种方式加载依赖文件:
  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
举报

相关推荐

0 条评论