python制作的软件工具安装包

思考:

  • 如何将制作的软件工具分发给用户?
  • 打成压缩包分发给用户;
  • 制作安装程序
  • 本文介绍了一种将软件工具打包为独立安装程序的方法。核心思路是将目录文件转换为Base64编码的JSON文件,再通过解码程序还原为可执行文件。

内容说明

主要包含两个Python脚本:directory_to_base64.py将指定目录转换为包含文件内容的JSON文件;base64_to_directory.py则执行反向操作,将JSON还原为目录结构。文章详细说明了使用PyInstaller打包时的注意事项,包括资源文件路径处理、版本信息配置等,并提供了完整的示例代码和spec文件配置模板。这种方法适用于需要将多个文件打包为单一可执行安装程序的场景。

核心代码

python 复制代码
'''directory_to_base64.py'''

import os
import base64
import json
import argparse
from pathlib import Path

def file_to_base64(file_path):
    """将文件内容转换为 Base64 编码"""
    try:
        with open(file_path, 'rb') as file:
            content = file.read()
            return base64.b64encode(content).decode('utf-8')
    except Exception as e:
        print(f"Error reading file {file_path}: {e}")
        return None

def directory_to_dict(dir_path):
    """递归将目录结构转换为字典"""
    result = {}
    for item in os.listdir(dir_path):
        item_path = os.path.join(dir_path, item)
        if os.path.isfile(item_path):
            base64_content = file_to_base64(item_path)
            if base64_content is not None:
                result[item] = {
                    'type': 'file',
                    'content': base64_content
                }
        elif os.path.isdir(item_path):
            result[item] = {
                'type': 'directory',
                'content': directory_to_dict(item_path)
            }
    return result

def main():
    parser = argparse.ArgumentParser(description='Convert a directory and its contents to Base64 encoded JSON')
    parser.add_argument('--input', '-i', required=True, help='Input directory path')
    parser.add_argument('--output', '-o', required=True, help='Output JSON file path')
    args = parser.parse_args()

    input_dir = args.input
    output_file = args.output

    if not os.path.isdir(input_dir):
        print(f"Error: Input directory '{input_dir}' does not exist or is not a directory.")
        return

    try:
        # 创建输出文件的目录(如果不存在)
        output_dir = os.path.dirname(output_file)
        if output_dir:
            os.makedirs(output_dir, exist_ok=True)

        # 转换目录结构为字典
        dir_dict = directory_to_dict(input_dir)

        # 添加元数据
        metadata = {
            'root_directory': os.path.basename(os.path.abspath(input_dir)),
            'total_files': sum(1 for _ in Path(input_dir).rglob('*') if os.path.isfile(_)),
            'total_directories': sum(1 for _ in Path(input_dir).rglob('*') if os.path.isdir(_))
        }

        result = {
            'metadata': metadata,
            'content': dir_dict
        }

        # 写入 JSON 文件
        with open(output_file, 'w') as f:
            json.dump(result, f, indent=2)

        print(f"Successfully converted directory '{input_dir}' to Base64 encoded JSON in '{output_file}'.")

    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    main()    
python 复制代码
'''base64_to_directory.py'''

import os
import base64
import json
import argparse
import sys
import pyexpat

from pyexpat.errors import messages


def decode_base64_to_file(base64_content, output_path):
    """将 Base64 编码内容写入文件"""
    try:
        with open(output_path, 'wb') as file:
            file.write(base64.b64decode(base64_content))
        return True
    except Exception as e:
        print(f"Error writing file {output_path}: {e}")
        return False


def process_directory(content_dict, output_dir):
    """递归处理目录结构并还原文件"""
    for item_name, item_data in content_dict.items():
        item_path = os.path.join(output_dir, item_name)

        if item_data['type'] == 'file':
            # 创建文件
            base64_content = item_data['content']
            if decode_base64_to_file(base64_content, item_path):
                print(f"Created file: {item_path}")
        elif item_data['type'] == 'directory':
            # 创建目录
            os.makedirs(item_path, exist_ok=True)
            print(f"Created directory: {item_path}")
            # 递归处理子目录
            process_directory(item_data['content'], item_path)

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)

def main():
    """    parser = argparse.ArgumentParser(description='Decode a Base64 encoded JSON file back to directory structure')
    parser.add_argument('--input', '-i', required=True, help='Input JSON file path')
    parser.add_argument('--output', '-o', required=True, help='Output directory path')
    args = parser.parse_args()"""

    # 使用示例
    input_file = resource_path("input.json")
    output_dir = "./Tool"

    """if not os.path.isfile(input_file):
        print(f"Error: Input file '{input_file}' does not exist or is not a file.")
        return
"""
    try:
        # 读取 JSON 文件
        with open(input_file, 'r') as f:
            data = json.load(f)

        # 创建输出目录(如果不存在)
        os.makedirs(output_dir, exist_ok=True)

        # 获取元数据
        metadata = data.get('metadata', {})
        root_directory = metadata.get('root_directory', '')
        total_files = metadata.get('total_files', 0)
        total_dirs = metadata.get('total_directories', 0)

        print(f"Decoding directory: {root_directory}")
        print(f"Total files expected: {total_files}")
        print(f"Total directories expected: {total_dirs}")
        print(f"Output location: {output_dir}")
        print("Starting decoding process...")

        # 处理内容
        content_dict = data.get('content', {})
        process_directory(content_dict, output_dir)

        print("Decoding completed successfully!")

    except Exception as e:
        print(f"An error occurred: {e}")


if __name__ == "__main__":
    main()
XML 复制代码
#base64_to_directory.spec

# -*- mode: python ; coding: utf-8 -*-

a = Analysis(
    ['D:\\workSpace\\python_work\\learn\\STtest\\build\\base64_to_directory.py'],
    pathex=[],
    binaries=[],
    datas=[('input.json', '.')],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='csvfileBatchGenerationToolInstall',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    version='version.txt'     # 指定版本信息文件
)
XML 复制代码
# version.txt
# 版本信息文件
VSVersionInfo(
  ffi=FixedFileInfo(
    filevers=(1, 0, 0, 0),
    prodvers=(1, 0, 0, 0),
    mask=0x3f,
    flags=0x0,
    OS=0x40004,
    fileType=0x1,
    subtype=0x0,
    date=(0, 0)
  ),
  kids=[
    StringFileInfo(
      [
        StringTable(
          '080404B0',
          [
            StringStruct('CompanyName', ''),
            StringStruct('FileDescription','工具安装程序'),
            StringStruct('FileVersion', '1.0.0'),
            StringStruct('InternalName', 'csv_data_install'),
            StringStruct('LegalCopyright', '© 2025 Company. All rights reserved.'),
            StringStruct('OriginalFilename', 'install.exe'),
            StringStruct('ProductName', 'ToolInstall'),
            StringStruct('ProductVersion', '1.0.0')
          ]
        )
      ]
    ),
    VarFileInfo(
      [
        VarStruct('Translation', [2052, 1200])  # 2052=中文
      ]
    )
  ]
)

注意事项

input.json文件作为数据资源文件,需要通过os.path.join(sys._MEIPASS, relative_path)设定打包后的路径。datas=[('input.json', '.')]指定资源文件('原路径','目标路径')。执行命令pyinstaller "spec文件路径"打包。

相关推荐
大模型真好玩11 分钟前
深入浅出LangGraph AI Agent智能体开发教程(五)—LangGraph 数据分析助手智能体项目实战
人工智能·python·mcp
测试老哥16 分钟前
Selenium 使用指南
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
百锦再41 分钟前
[特殊字符] Python在CentOS系统执行深度指南
开发语言·python·plotly·django·centos·virtualenv·pygame
张子夜 iiii1 小时前
4步OpenCV-----扫秒身份证号
人工智能·python·opencv·计算机视觉
潮汐退涨月冷风霜2 小时前
数字图像处理(1)OpenCV C++ & Opencv Python显示图像和视频
c++·python·opencv
酷飞飞8 小时前
Python网络与多任务编程:TCP/UDP实战指南
网络·python·tcp/ip
数字化顾问9 小时前
Python:OpenCV 教程——从传统视觉到深度学习:YOLOv8 与 OpenCV DNN 模块协同实现工业缺陷检测
python
学生信的大叔10 小时前
【Python自动化】Ubuntu24.04配置Selenium并测试
python·selenium·自动化
诗句藏于尽头11 小时前
Django模型与数据库表映射的两种方式
数据库·python·django
智数研析社12 小时前
9120 部 TMDb 高分电影数据集 | 7 列全维度指标 (评分 / 热度 / 剧情)+API 权威源 | 电影趋势分析 / 推荐系统 / NLP 建模用
大数据·人工智能·python·深度学习·数据分析·数据集·数据清洗