基于python自制一個軟件工具安裝包
思考
如何將制作的軟件工具分發(fā)給用戶?
打成壓縮包分發(fā)給用戶;
制作安裝程序
本文介紹了一種將軟件工具打包為獨立安裝程序的方法。核心思路是將目錄文件轉(zhuǎn)換為Base64編碼的JSON文件,再通過解碼程序還原為可執(zhí)行文件。
內(nèi)容說明
主要包含兩個Python腳本:directory_to_base64.py將指定目錄轉(zhuǎn)換為包含文件內(nèi)容的JSON文件;base64_to_directory.py則執(zhí)行反向操作,將JSON還原為目錄結(jié)構(gòu)。文章詳細(xì)說明了使用PyInstaller打包時的注意事項,包括資源文件路徑處理、版本信息配置等,并提供了完整的示例代碼和spec文件配置模板。這種方法適用于需要將多個文件打包為單一可執(zhí)行安裝程序的場景。
核心代碼
directory_to_base64.py
import os
import base64
import json
import argparse
from pathlib import Path
def file_to_base64(file_path):
"""將文件內(nèi)容轉(zhuǎn)換為 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):
"""遞歸將目錄結(jié)構(gòu)轉(zhuǎn)換為字典"""
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:
# 創(chuàng)建輸出文件的目錄(如果不存在)
output_dir = os.path.dirname(output_file)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
# 轉(zhuǎn)換目錄結(jié)構(gòu)為字典
dir_dict = directory_to_dict(input_dir)
# 添加元數(shù)據(jù)
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 編碼內(nèi)容寫入文件"""
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):
"""遞歸處理目錄結(jié)構(gòu)并還原文件"""
for item_name, item_data in content_dict.items():
item_path = os.path.join(output_dir, item_name)
if item_data['type'] == 'file':
# 創(chuàng)建文件
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':
# 創(chuàng)建目錄
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):
"""獲取資源的絕對路徑,適應(yīng)打包后的環(huán)境"""
if hasattr(sys, '_MEIPASS'):
# 打包后的環(huán)境
return os.path.join(sys._MEIPASS, relative_path)
# 開發(fā)環(huán)境
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)
# 創(chuàng)建輸出目錄(如果不存在)
os.makedirs(output_dir, exist_ok=True)
# 獲取元數(shù)據(jù)
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...")
# 處理內(nèi)容
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文件作為數(shù)據(jù)資源文件,需要通過os.path.join(sys._MEIPASS, relative_path)設(shè)定打包后的路徑。datas=[('input.json', '.')]指定資源文件(‘原路徑’,‘目標(biāo)路徑’)。執(zhí)行命令pyinstaller "spec文件路徑"打包。
到此這篇關(guān)于基于python自制一個軟件工具安裝包的文章就介紹到這了,更多相關(guān)python軟件安裝包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于flask路由app.route及路由參數(shù)的各種用法解析
我們在開發(fā)過程中,編寫項目時所使用的路由往往是指代了框架/項目中用于完成路由功能的類,這個類一般就是路由類,簡稱路由,這篇文章主要介紹了有關(guān)flask路由app.route及路由參數(shù)的各種用法解析,需要的朋友可以參考下2024-03-03
python 實現(xiàn)調(diào)用子文件下的模塊方法
今天小編就為大家分享一篇python 實現(xiàn)調(diào)用子文件下的模塊方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12
Python?Pygame實戰(zhàn)之五款童年經(jīng)典游戲合集
本文為大家總結(jié)了五款利用Python+Pygame實現(xiàn)的童年經(jīng)典游戲:推箱子、滑雪、八分音符醬、保衛(wèi)蘿卜和飛機大戰(zhàn),快跟隨小編一起學(xué)習(xí)一下2022-04-04
django傳值給模板, 再用JS接收并進(jìn)行操作的實例
今天小編就為大家分享一篇django傳值給模板, 再用JS接收并進(jìn)行操作的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05

