在上一篇文章中,我们梳理了如果编写一个启动本地服务的py代码。但执行它需要有python环境。
为了方便拷贝到没有python环境的电脑使用,考虑把它打包成exe(windows系统电脑)。
1 代码补充
python.py的代码对资源路径做补充处理:
import sys
import os
import http.server
import socketserver
# 定义服务器端口
PORT = 50000
# 获取打包后的资源路径
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
# 定义 HTML 页面文件(使用资源路径)
index_file = resource_path('iat-js-demo/example/wxl/index.html')
class MyHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.path = '/' + index_file
return super().do_GET()
with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
print(f"服务器已启动,访问地址: http://localhost:{PORT}")
# 启动服务器,开始监听请求
httpd.serve_forever()
2 安装pyinstaller
安装打包工具(在项目目录中执行):
# 这个成功率比较低
pip install pyinstaller
# 阿里云镜像源,成功率高
pip install pyinstaller -i https://mirrors.aliyun.com/pypi/simple/
# 清华大学镜像源
pip install pyinstaller -i https://pypi.tuna.tsinghua.edu.cn/simple
3 打包程序
在命令行中,切换到 server.py 文件所在的目录,然后执行以下命令进行打包:
pyinstaller --onefile --add-data "js-demo;js-demo" -n server server.py
- --onefile :将所有依赖项打包成一个单独的可执行文件。
--add-data "js-demo;js-demo" :将 js-demo 目录下的所有文件添加到打包后的程序中,并保持目录结构不变。 - -n server :指定打包后的可执行文件的名称为 server.exe 。
打包成功后,会在目录生成一个dist文件夹,文件夹里面有一个server.exe。
(build 目录会在打包过程中生成,它主要包含一些打包过程中产生的临时文件和中间文件。)
4 拷贝html
把要访问的html网页拷贝到server.exe同级目录。
5 测试访问网页
双击server.exe,启动本地服务,监听端口在50000.
在浏览器中输入localhost:50000/index.html
即可访问网页。
6 其他:打包无控制台的exe
若要打包没有命令行窗口的exe,可以加上参数--noconsole
。
pyinstaller --onefile --noconsole -n ServerApp server.py
但要注意当使用 --noconsole 选项打包 Python 脚本为 EXE 文件时,程序会在无控制台模式下运行,这可能会导致一些依赖于控制台输出或标准输入输出的代码出现问题。如果发现报错信息'NoneType' object has no attribute 'write' ,通常与标准输出或日志记录相关,因为在无控制台模式下,标准输出( sys.stdout )和标准错误( sys.stderr )可能会被设置为 None 。
问题分析
在无控制台模式下, SimpleHTTPRequestHandler 类可能会尝试使用标准输出进行日志记录或错误处理,而此时标准输出为 None ,从而导致错误。
解决方案
可以通过重定向标准输出和标准错误到日志文件来解决这个问题。在代码中添加以下逻辑,确保在无控制台模式下也能正常处理日志记录。
# 重定向标准输出和标准错误到日志文件
if hasattr(sys, '_MEIPASS'): # 检查是否为打包环境
sys.stdout = open(os.devnull, 'w')
sys.stderr = open('server_error.log', 'w')
参考内容:
通过Trae(CN)Chat模式下完成该流程。