cxfreeze打包python程式的方法說明(生成安裝包,實現桌面快捷方式、刪除快捷方式)

来源:https://www.cnblogs.com/panfb/archive/2018/09/10/9623083.html
-Advertisement-
Play Games

一、cxfreeze基礎 1、cxfreeze功能 python代碼文件轉exe方法有三種,分別是cx_freeze,py2exe,PyInstaller,這三種方式各有千秋,本人只用過py2exe和cxfreeze,這裡重點說明cxfreeze。 2、安裝包下載地址 https://sourcef ...


一、cxfreeze基礎

 1、cxfreeze功能

python代碼文件轉exe方法有三種,分別是cx_freeze,py2exe,PyInstaller,這三種方式各有千秋,本人只用過py2exe和cxfreeze,這裡重點說明cxfreeze。

 

2、安裝包下載地址

 https://sourceforge.net/projects/cx-freeze/files/

 

3、cxfree的官方說明文檔

 http://cx-freeze.readthedocs.io/en/latest/distutils.html

 

二、cxfreeze使用方法

1、cxfreeze命令方法

cxfreeze etax.py --target-dir out/      #把etax.py 打包成etax.exe,放在out目錄下 

 

2、編寫cxsetup.py編譯腳本,然後用py去執行。

來一個簡單的需求:編譯etax.py生成test.exe文件。

a、步驟1,先編寫一個cxsetup.py腳本文件

複製代碼 #coding=utf-8
#cxsetup.py代碼
from cx_Freeze import setup, Executable
setup(
    name="test",
    version="1.0",
    description="Test application",
    author="zhongtang",
    executables=[Executable("etax.py")]
)
複製代碼

可以看到,cxsetup.py其實是一個py程式,該程式調用了cx_Freeze 包中的setup、Executable類。

然後用python執行cxsetup.py,就可以實現編譯exe。

另外補充一點,cxsetup.py可以隨意起名,預設都叫xxsetup.py

 

編譯後的文件屬性如下:

 

 

 b、步驟2,執行py命令

#build方式打包成exe文件,可以脫離python環境運行

python cxsetup.py build 

 

#bdist_msi方式可以打包成windows下msi格式的安裝包文件

python cxsetup.py bdist_msi 

 

三、cxsetup.py程式的進階寫法

還是以一個實例說明,需求如下:

1、et是一個基於wxpython編寫的圖形界面的小程式

2、et中使用了ini配置文件,文件名為et.ini

3、et中使用了PIL類,並使用圖片文件et.jpg

4、et程式一共包含4個文件,主程式名為eTMain.py

5、打包py成exe,脫離python環境運行

6、生成windows下的msi安裝包,該安裝包運行後會安裝桌面快捷方式、開始菜單快捷方式、刪除程式的快捷方式,並且開始菜單有子目錄。

 

 上cxsetup.py代碼

複製代碼 #!/usr/bin/python
#coding=utf-8
# create by :joshua zou 2016.7.23

import sys
import traceback
from cx_Freeze import setup, Executable
import msilib

# Dependencies are automatically detected, but it might need fine tuning.

#中文需要顯式用gbk方式編碼
product_name = u'異體'.encode('gbk')
unproduct_name = u'卸載異體'.encode('gbk')
product_desc = u"異體客戶端程式 Ver1.0".encode("gbk")

#uuid叫通用唯一識別碼,後面再卸載快捷方式中要用到
product_code = msilib.gen_uuid()
#主程式手動命名
target_name= 'etMain.exe'


build_exe_options = {
    "include_files":["et.ini","et.jpg",'data'],    
    #包含外圍的ini、jpg文件,以及data目錄下所有文件,以上所有的文件路徑都是相對於cxsetup.py的路徑。
    "packages": ["os","wx"],                #包含用到的包
    "includes": ["PIL","traceback"], 
    "excludes": ["tkinter"],                #提出wx里tkinter包
    "path": sys.path,                       #指定上述的尋找路徑
    "icon": "et.ico"                        #指定ico文件
};

#快捷方式表,這裡定義了三個快捷方式
shortcut_table = [
     
     #1、桌面快捷方式
    ("DesktopShortcut",           # Shortcut
     "DesktopFolder",             # Directory_ ,必須在Directory表中
     product_name,                # Name
     "TARGETDIR",                 # Component_,必須在Component表中
     "[TARGETDIR]"+target_name,   # Target
     None,                        # Arguments
     product_desc,                # Description
     None,                        # Hotkey
     None,                        # Icon
     None,                        # IconIndex
     None,                        # ShowCmd
     'TARGETDIR'                  # WkDir
     ),
    
    #2、開始菜單快捷方式
    ("StartupShortcut",           # Shortcut
     "MenuDir",                   # Directory_
     product_name,                # Name
     "TARGETDIR",                 # Component_
     "[TARGETDIR]"+target_name,   # Target
     None,                        # Arguments
     product_desc,                # Description
     None,                        # Hotkey
     None,                        # Icon
     None,                        # IconIndex
     None,                        # ShowCmd
     'TARGETDIR'                  # WkDir
     ),
    
    #3、程式卸載快捷方式
    ("UniShortcut",              # Shortcut
     "MenuDir",                  # Directory_
     unproduct_name,             # Name
     "TARGETDIR",                # Component_
     "[System64Folder]msiexec.exe",  # Target
     r"/x"+product_code,         # Arguments
     product_desc,               # Description      None,                       # Hotkey      None,                       # Icon      None,                       # IconIndex      None,                       # ShowCmd      'TARGETDIR'                 # WkDir      )           ] #手動建設的目錄,在這裡定義。 ''' 自定義目錄說明: ============== 1、3個欄位分別為 Directory,Directory_Parent,DefaultDir 2、欄位1指目錄名,可以隨意命名,併在後面直接使用 3、欄位2是指欄位1的上級目錄,上級目錄本身也是需要預先定義,除了某些系統自動定義的目錄,譬如桌面快捷方式中使用DesktopFolder 參考網址 https://msdn.microsoft.com/en-us/library/aa372452(v=vs.85).aspx ''' directories = [      ( "ProgramMenuFolder","TARGETDIR","." ),      ( "MenuDir", "ProgramMenuFolder", product_name)      ] # Now create the table dictionary # 也可把directories放到data里。 ''' 快捷方式說明: ============ 1、windows的msi安裝包文件,本身都帶一個install database,包含很多表(用一個Orca軟體可以看到)。 2、下麵的 Directory、Shortcut都是msi資料庫中的表,所以冒號前面的名字是固定的(貌似大小寫是區分的)。 3、data節點其實是擴展很多自定義的東西,譬如前面的directories的配置,其實cxfreeze中代碼的內容之一,就是把相關配置數據寫入到msi資料庫的對應表中 參考網址:https://msdn.microsoft.com/en-us/library/aa367441(v=vs.85).aspx ''' msi_data = {#"Directory":directories ,             "Shortcut": shortcut_table            } # Change some default MSI options and specify the use of the above defined tables #註意product_code是我擴展的,現有的官網cx_freeze不支持該參數,為此簡單修改了cx_freeze包的代碼,後面貼上修改的代碼。 bdist_msi_options = { 'data': msi_data,                       'upgrade_code': '{9f21e33d-48f7-cf34-33e9-efcfd80eed10}',                       'add_to_path': False,                       'directories': directories,                       'product_code': product_code,                       'initial_target_dir': r'[ProgramFilesFolder]\%s' % (product_name)}                        # GUI applications require a different base on Windows (the default is for a # console application). base = None; if sys.platform == "win32":      base = "Win32GUI" #簡易方式定義快捷方式,放到Executeable()里。 #shortcutName = "AppName", #shortcutDir = "ProgramMenuFolder"  setup(  name = "et",         author='et china corp',         version = "1.0",         description = product_desc.decode('gbk'),         options = {"build_exe": build_exe_options,                    "bdist_msi": bdist_msi_options},         executables = [Executable("etMain.py",                                   targetName= target_name,                                   compress = True,                                    base=base)                        ]) 
複製代碼

 

四、補充說明

1、有關windows install msi 文件

可以去microsoft的官網學習學習,https://msdn.microsoft.com/en-us/library/aa372860(v=vs.85).aspx

 

2、Orca編輯工具

查看修改msi文件資料庫表的工具,Orca(msi編輯工具) 4.5.6 中文綠色版 。

絕對堪稱神器,貼個圖片,這玩意太棒了(本文很多寫法就是仿照python2.7的安裝文件的數據,結合cxfree代碼琢磨出來的)。

 

 

3、擴展的cxfreeze代碼

前文在cxsetup.exe中我提到自定義了product_code參數,這個參數在官方版本的cxfreeze是不支持的(官方版本的productcode是直接寫死的代碼msilib.gen_uuid())。

所以擴展product_code配置的目的,就是因為在卸載Shortcut,需要用到 msiexec.exe /x {productcode}。

 

修改原理:

將 msilib.gen_uuid()放到cxsetup.py中,並作為product_code參數傳給cxfreeze。

在cxfreeze中判斷product_code參數是否定義,沒定義則預設取msilib.gen_uuid(),有定義則使用定義值。

修改點:

cx_Free/windist.py文件。

修改點1、

class bdist_msi(distutils.command.bdist_msi.bdist_msi):
    user_options = distutils.command.bdist_msi.bdist_msi.user_options + [
        ('add-to-path=', None, 'add target dir to PATH environment variable'),
        ('upgrade-code=', None, 'upgrade code to use'),
        ('initial-target-dir=', None, 'initial target directory'),
        ('target-name=', None, 'name of the file to create'),
        ('directories=', None, 'list of 3-tuples of directories to create'),
        ('data=', None, 'dictionary of data indexed by table name'),
        # add by joshua zou 2016.07.23
        ('product-code=', None, 'product code to use')
    ]

 修改點2、

    def finalize_options(self):
        distutils.command.bdist_msi.bdist_msi.finalize_options(self)
        name = self.distribution.get_name()
        fullname = self.distribution.get_fullname()
        if self.initial_target_dir is None:
            if distutils.util.get_platform() == "win-amd64":
                programFilesFolder = "ProgramFiles64Folder"
            else:
                programFilesFolder = "ProgramFilesFolder"
            self.initial_target_dir = r"[%s]\%s" % (programFilesFolder, name)
        if self.add_to_path is None:
            self.add_to_path = False
        if self.target_name is None:
            self.target_name = fullname
        if not self.target_name.lower().endswith(".msi"):
            platform = distutils.util.get_platform().replace("win-", "")
            self.target_name = "%s-%s.msi" % (self.target_name, platform)
        if not os.path.isabs(self.target_name):
            self.target_name = os.path.join(self.dist_dir, self.target_name)
        if self.directories is None:
            self.directories = []
        if self.data is None:
            self.data = {}
        # add by joshua zou 2016.7
        if self.product_code is None:
            self.product_code = msilib.gen_uuid()

修改點3、

  def initialize_options(self):
        distutils.command.bdist_msi.bdist_msi.initialize_options(self)
        self.upgrade_code = None
        self.add_to_path = None
        self.initial_target_dir = None
        self.target_name = None
        self.directories = None
        self.data = None
        # add by joshua zou 2016.7
        self.product_code=None

代碼點4、

    def run(self):
        if not self.skip_build:
            self.run_command('build')
        install = self.reinitialize_command('install', reinit_subcommands = 1)
        install.prefix = self.bdist_dir
        install.skip_build = self.skip_build
        install.warn_dir = 0
        distutils.log.info("installing to %s", self.bdist_dir)
        install.ensure_finalized()
        install.run()
        self.mkpath(self.dist_dir)
        fullname = self.distribution.get_fullname()
        if os.path.exists(self.target_name):
            os.unlink(self.target_name)
        metadata = self.distribution.metadata
        author = metadata.author or metadata.maintainer or "UNKNOWN"
        version = metadata.get_version()
        sversion = "%d.%d.%d" % \
                distutils.version.StrictVersion(version).version
        '''
        modified by joshua zou 2016.7
        self.db = msilib.init_database(self.target_name, msilib.schema,
                self.distribution.metadata.name, msilib.gen_uuid(), sversion,
                author)
        '''
        self.db = msilib.init_database(self.target_name, msilib.schema,
                        self.distribution.metadata.name, self.product_code, sversion,
                        author)       
        msilib.add_tables(self.db, msilib.sequence)

 

完整源碼

複製代碼 import distutils.command.bdist_msi
import distutils.errors
import distutils.util
import msilib
import os

__all__ = [ "bdist_msi" ]

# force the remove existing products action to happen first since Windows
# installer appears to be braindead and doesn't handle files shared between
# different "products" very well
sequence = msilib.sequence.InstallExecuteSequence

for index, info in enumerate(sequence):
    if info[0] == 'RemoveExistingProducts':
        sequence[index] = (info[0], info[1], 1450)


class bdist_msi(distutils.command.bdist_msi.bdist_msi):
    user_options = distutils.command.bdist_msi.bdist_msi.user_options + [
        ('add-to-path=', None, 'add target dir to PATH environment variable'),
        ('upgrade-code=', None, 'upgrade code to use'),
        ('initial-target-dir=', None, 'initial target directory'),
        ('target-name=', None, 'name of the file to create'),
        ('directories=', None, 'list of 3-tuples of directories to create'),
        ('data=', None, 'dictionary of data indexed by table name'),
        # add by joshua zou 2016.07.23
        ('product-code=', None, 'product code to use')
    ]
    x = y = 50
    width = 370
    height = 300
    title = "[ProductName] Setup"
    modeless = 1
    modal = 3

    def add_config(self, fullname):
        if self.add_to_path:
            msilib.add_data(self.db, 'Environment',
                    [("E_PATH", "Path", r"[~];[TARGETDIR]", "TARGETDIR")])
        if self.directories:
            msilib.add_data(self.db, "Directory", self.directories)
        msilib.add_data(self.db, 'CustomAction',
                [("A_SET_TARGET_DIR", 256 + 51, "TARGETDIR",
                        self.initial_target_dir)])
        msilib.add_data(self.db, 'InstallExecuteSequence',
                [("A_SET_TARGET_DIR", 'TARGETDIR=""', 401)])
        msilib.add_data(self.db, 'InstallUISequence',
                [("PrepareDlg", None, 140),
                 ("A_SET_TARGET_DIR", 'TARGETDIR=""', 401),
                 ("SelectDirectoryDlg", "not Installed", 1230),
                 ("MaintenanceTypeDlg",
                        "Installed and not Resume and not Preselected", 1250),
                 ("ProgressDlg", None, 1280)
                ])
        for index, executable in enumerate(self.distribution.executables):
            if executable.shortcutName is not None \
                    and executable.shortcutDir is not None:
                baseName = os.path.basename(executable.targetName)
                msilib.add_data(self.db, "Shortcut",
                        [("S_APP_%s" % index, executable.shortcutDir,
                                executable.shortcutName, "TARGETDIR",
                                "[TARGETDIR]%s" % baseName, None, None, None,
                                None, None, None, None)])
        for tableName, data in self.data.items():
            msilib.add_data(self.db, tableName, data)

    def add_cancel_dialog(self):
        dialog = msilib.Dialog(self.db, "CancelDlg", 50, 10, 260, 85, 3,
                self.title, "No", "No", "No")
        dialog.text("Text", 48, 15, 194, 30, 3,
                "Are you sure you want to cancel [ProductName] installation?")
        button = dialog.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
        button.event("EndDialog", "Exit")         button = dialog.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")         button.event("EndDialog", "Return")     def add_error_dialog(self):         dialog = msilib.Dialog(self.db, "ErrorDlg", 50, 10, 330, 101, 65543,                 self.title, "ErrorText", None, None)         dialog.text("ErrorText", 50, 9, 280, 48, 3, "")         for text, x in [("No", 120), ("Yes", 240), ("Abort", 0),                 ("Cancel", 42), ("Ignore", 81), ("Ok", 159), ("Retry", 198)]:             button = dialog.pushbutton(text[0], x, 72, 81, 21, 3, text, None)             button.event("EndDialog", "Error%s" % text)     def add_exit_dialog(self):         dialog = distutils.command.bdist_msi.PyDialog(self.db, "ExitDialog",                 self.x, self.y, self.width, self.height, self.modal,                 self.title, "Finish", "Finish", "Finish")         dialog.title("Completing the [ProductName] installer")         dialog.back("< Back", "Finish", active = False)         dialog.cancel("Cancel", "Back", active = False)         dialog.text("Description", 15, 235, 320, 20, 0x30003,                 "Click the Finish button to exit the installer.")         button = dialog.next("Finish", "Cancel", name = "Finish")         button.event("EndDialog", "Return")     def add_fatal_error_dialog(self):         dialog = distutils.command.bdist_msi.PyDialog(self.db, "FatalError",                 self.x, self.y, self.width, self.height, self.modal,                 self.title, "Finish", "Finish", "Finish")         dialog.title("[ProductName] installer ended prematurely")         dialog.back("< Back", "Finish", active = False)         dialog.cancel("Cancel", "Back", active = False)         dialog.text("Description1", 15, 70, 320, 80, 0x30003,                 "[ProductName] setup ended prematurely because of an error. "                 "Your system has not been modified. To install this program "                 "at a later time, please run the installation again.")         dialog.text("Description2", 15, 155, 320, 20, 0x30003,                 "Click the Finish button to exit the installer.")         button = dialog.next("Finish", "Cancel", name = "Finish")         button.event("EndDialog", "Exit")     def add_files(self):         db = self.db         cab = msilib.CAB("distfiles")         f = msilib.Feature(db, "default", "Default Feature", "Everything", 1,                 directory="TARGETDIR")         f.set_current()         rootdir = os.path.abspath(self.bdist_dir)         root = msilib.Directory(db, cab, None, rootdir, "TARGETDIR",                 "SourceDir")         db.Commit()         todo = [root]         while todo:             dir = todo.pop()             for file in os.listdir(dir.absolute):                 if os.path.isdir(os.path.join(dir.absolute, file)):                     newDir = msilib.Directory(db, cab, dir, file, file,                             "%s|%s" % (dir.make_short(file), file))                     todo.append(newDir)                 else:                     dir.add_file(file)         cab.commit(db)     def add_files_in_use_dialog(self):         dialog = distutils.command.bdist_msi.PyDialog(self.db, "FilesInUse",                 self.x, self.y, self.width, self.height, 19, self.title,                 "Retry", "Retry", "Retry", bitmap = False)         dialog.text("Title", 15, 6, 200, 15, 0x30003,                 r"{\DlgFontBold8}Files in Use")         dialog.text("Description", 20, 23, 280, 20, 0x30003,                 "Some files that need to be updated are currently in use.")         dialog.text("Text", 20, 55, 330, 50, 3,                 "The following applications are using files that need to be "                 "updated by this setup. Close these applications and then "                 "click Retry to continue the installation or Cancel to exit "                 "it.")         dialog.control("List", "ListBox", 20, 107, 330, 130, 7,                 "FileInUseProcess", None, None, None)         button = dialog.back("Exit", "Ignore", name = "Exit")         button.event("EndDialog", "Exit")         button = dialog.next("Ignore", "Retry", name = "Ignore")         button.event("EndDialog", "Ignore")         button = dialog.cancel("Retry", "Exit", name = "Retry")         button.event("EndDialog", "Retry")     def add_maintenance_type_dialog(self):         dialog = distutils.command.bdist_msi.PyDialog(self.db,                 "MaintenanceTypeDlg", self.x, self.y, self.width, self.height,                 self.modal, self.title, "Next", "Next", "Cancel")         dialog.title("Welcome to the [ProductName] Setup Wizard")         dialog.text("BodyText", 15, 63, 330, 42, 3,                 "Select whether you want to repair or remove [ProductName].")         group = dialog.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,                 "MaintenanceForm_Action", "", "Next")         group.add("Repair", 0, 18, 300, 17, "&Repair [ProductName]")         group.add("Remove", 0, 36, 300, 17, "Re&move [ProductName]")         dialog.back("< Back", None, active = False)         button = dialog.next("Finish", "Cancel")         button.event("[REINSTALL]", "ALL",                 'MaintenanceForm_Action="Repair"', 5)         button.event("[Progress1]", "Repairing",                 'MaintenanceForm_Action="Repair"', 6)         button.event("[Progress2]", "repairs",                 'MaintenanceForm_Action="Repair"', 7)         button.event("Reinstall", "ALL",                 'MaintenanceForm_Action="Repair"', 8)         button.event("[REMOVE]", "ALL",                 'MaintenanceForm_Action="Remove"', 11)         button.event("[Progress1]", "Removing",                 'MaintenanceForm_Action="Remove"', 12)         button.event("[Progress2]", "removes",                 'MaintenanceForm_Action="Remove"', 13)         button.event("Remove", "ALL",                 'MaintenanceForm_Action="Remove"', 14)         button.event("EndDialog", "Return",                 'MaintenanceForm_Action<>"Change"', 20)         button = dialog.cancel("Cancel", "RepairRadioGroup")         button.event("SpawnDialog", "CancelDlg")     def add_prepare_dialog(self):         dialog = distutils.command.bdist_msi.PyDialog(self.db, "PrepareDlg",                 self.x, self.y, self.width, self.height, self.modeless,               
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C++ 的加強主要表現在:類型的加強、面向對象支持 1、C++改進 C++更強調語言的實用性,所有的變數都可以再需要使用的時候再定義,C語言中的變數都必須在作用域開始的位置定義 C++ int c = 0; for (int i = 1; i register 關鍵字請求編譯器將局部變數存儲與寄存器 ...
  • python print 函數(在python中,不區分 ' ' 和 " "): 或者 ...
  • c/c++ 標準庫 迭代器 begin和end運算符返回的具體類型由對象是否是常量決定,如果對象是常量,begin和end返回const_iterator;如果對象不是常量,返回iteraotor 1,但凡是使用了迭代器的迴圈體,都不要向迭代器所屬的容器添加元素。 2,不能在範圍for迴圈中向vec ...
  • 公司對外提供了http介面服務,涉及到了des加密,有客戶用Python開發,但搞不定加密,客戶的問題就是大問題,雖然以前沒接觸過Python,也只能硬著頭皮上,不停的baidu各種資料,從環境搭建、到hello world、最後到des加密實現,終於在半天之內幫客戶搞定。感嘆Python強大的類庫 ...
  • Output assignment statements in the output statement ...
  • 1、dir:列出當前目錄下的主體及文件夾。 2、md:創建目錄。 3、rd:刪除目錄。{註意:rd不能刪除非空的文件夾,並且只能用於文件夾的刪除} 3、cd :進入指定目錄。 4、cd . . :退出到上一級目錄。 5、cd \ :退出到根目錄。 6、echo:輸出的意思,如果用大於號的尖端指向文本 ...
  • 測試用類 一般的Jar包 生成class文件 在命令行中輸入下麵代碼: 如果有中文,報 編碼GBK的不可映射字元 的錯誤,執行下麵代碼: 打包 可運行的Jar包 需要藉助 manifest.mf 配置文件 manifest.mf文件中內容: 這裡有幾個點,需要註意一下: key和value之間,必須 ...
  • 1.設置MySQL時區,明確指定 MySQL 資料庫的時區,不使用引發誤解的 CST 2.設置pojo類Date類型欄位的JsonFormat 參考文獻: 資料庫時區問題:https://blog.csdn.net/qq_30553235/article/details/79612824 4種解決J ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...