思考
如何将制作的软件工具分发给用户?
打成压缩包分发给用户;
制作安装程序
本文介绍了一种将软件工具打包为独立安装程序的方法。核心思路是将目录文件转换为base64编码的json文件,再通过解码程序还原为可执行文件。
内容说明
主要包含两个python脚本:directory_to_base64.py将指定目录转换为包含文件内容的json文件;base64_to_directory.py则执行反向操作,将json还原为目录结构。文章详细说明了使用pyinstaller打包时的注意事项,包括资源文件路径处理、版本信息配置等,并提供了完整的示例代码和spec文件配置模板。这种方法适用于需要将多个文件打包为单一可执行安装程序的场景。
核心代码
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()
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()
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' # 指定版本信息文件 )
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文件路径"打包。
到此这篇关于基于python自制一个软件工具安装包的文章就介绍到这了,更多相关python软件安装包内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论