<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      基于Python的FastAPI后端開發框架如何使用PyInstaller 進行打包與部署

      我在隨筆《WxPython跨平臺開發框架之使用PyInstaller 進行打包處理》中介紹過如何使用PyInstaller 進行打包處理的一些過程和事項。我們基于Python的FastAPI后端應用,在實際開發的時候,直接運行main.py 進行調試即可,但是部署的時候,我們就需要把它們進行打包處理,這里首選PyInstaller 進行打包。本文詳細介紹了 如何使用 PyInstaller 對基于 Python 的 FastAPI 后端項目進行打包與部署,使其能夠在目標環境中以獨立可執行文件的形式運行,無需安裝 Python 解釋器或額外依賴。文章面向希望將 FastAPI 服務打包為獨立運行服務的開發者,特別適用于企業內部系統或需要簡化部署的場景。

      一、背景與目標

      • 說明為什么需要將 FastAPI 項目打包為可執行文件。

      • 對比傳統部署方式(如 uvicorn main:app)與 PyInstaller 打包方式的區別。

      • 適用場景:企業內網部署、Windows 服務、無 Python 環境的服務器等。

      二、環境準備

      • Python 版本要求(推薦 Python 3.12.4+)。

      • FastAPI 與依賴(fastapi, uvicorn, pydantic, sqlalchemy, 等)。

      • 安裝 PyInstaller:

      pip install pyinstaller

      三、FastAPI 項目結構示例

      展示一個典型的 FastAPI 項目結構:

      project/
      ├── app/
      │   ├── main.py
      │   ├── api/
      │   ├── core/
      │   ├── models/
      │   ├── services/
      │   └── __init__.py
      ├── requirements.txt

      并說明 main.py 中如何啟動服務,例如:

      import uvicorn
      from app.main import app
      
      if __name__ == "__main__":
          uvicorn.run(app, host="0.0.0.0", port=8000)

      四、使用 PyInstaller 打包

      PyInstaller是目前最流行的Python打包工具之一。它可以將Python腳本打包成獨立的可執行文件,支持Windows、Linux和macOS平臺。

      PyInstaller 有豐富的文檔,提供了詳細的使用說明和常見問題解答,你可以通過以下鏈接訪問:

      這些文檔和資源能幫助你深入了解 PyInstaller 的使用方式,并解決在打包過程中可能遇到的問題。

      打包后的可執行文件可以在沒有 Python 環境的機器上運行。PyInstaller 會自動分析程序的依賴關系,并將所有必要的庫和資源打包到一個文件或者一個文件夾中。

      打包過程中,PyInstaller 會生成一個 .spec 文件。這個文件包含了 PyInstaller 的配置信息,其中包含了構建過程的所有配置信息。你可以修改這個文件來定制打包過程。

      如果我們執行下面代碼

      pyinstaller main.py

      或者指定更多的參數的代碼

      pyinstaller --onefile --icon=your_icon.ico main.py

      PyInstaller 都會生成一個 .spec 文件,然后可以編輯 main.spec 文件,以便進行更好的控制管理打包文件。

      雖然原則上.spec文件支持跨平臺的配置,不過我們在實際中往往根據不同的平臺配置特定的.spec文件。

      你可以手動修改 .spec 文件來添加資源文件、修改導入模塊、定制輸出路徑等。

      你可以通過編輯.spec 文件,在EXE、COLLECT和BUNDLE塊下添加一個name= ,為PyInstaller提供一個更好的名字,以便為應用程序(和dist 文件夾)使用。

      EXE下的名字是可執行文件的名字,BUNDLE下的名字是應用程序包的名字。

      import sys
      import os
      from pathlib import Path
      
      # 本文件用于Window平臺下打包整個項目,生成一個獨立的exe文件,依賴文件松散組合
      # 執行命令:pyinstaller main_my.spec
      # 打包后生成文件:dist\fastapi_app\fastapi_app.exe
      # 運行后,會在當前目錄生成一個 dist 文件夾,里面有 fastapi_app.exe 文件,在命令行窗口運行該文件即可啟動服務。
      
      
      if sys.platform == "win32":
          icon = "app/images/app.ico"
      elif sys.platform == "darwin":
          icon = "app/images/app.icns"
      
      block_cipher = None
      
      # 導入 PyInstaller 模塊
      from PyInstaller.building.build_main import Analysis
      from PyInstaller.building.build_main import PYZ
      from PyInstaller.building.build_main import EXE
      from PyInstaller.building.build_main import COLLECT
      
      # Analysis: PyInstaller Analysis object
      a = Analysis(
          ["app/main.py"],
          pathex=[],
          binaries=[],
          datas=[
              ("app/uvicorn_config.json", "app"),
              ("app/.env", "."),
              ("app/images/*", "app/images"),
              ("app/templates/*", "app/templates"),
              ("app/uploadfiles/*", "app/uploadfiles"),
              ("app/logs/*", "app/logs"),
          ],
          hiddenimports=[
               "uvicorn", "fastapi", "pydantic", "aiomysql", 'asyncio',   # 確保依賴被正確包含
          ],
          hookspath=[],
          hooksconfig={},
          runtime_hooks=[],
          excludes=[],
          win_no_prefer_redirects=False,
          win_private_assemblies=False,
          cipher=block_cipher,
          noarchive=False,
          optimize=0,
      )
      
      # PYZ: PyInstaller PYZ object
      pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

      修改完成后,執行以下命令來重新打包:

      pyinstaller main_my.spec

      如果我們想在Windows平臺生成的dist目錄中生成一個啟動exe,和其他相關的Lib依賴庫目錄,那么我們可以適當調整下.spec文件,讓它可以生成松散結構的文件目錄包。

      exe = EXE(
          pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name="fastapi_app",
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=True,          # True = 有控制臺輸出(調試方便),False = 靜默運行
          onefile=False,  # <-- False取消、True使用 onefile 模式
          icon=icon,  # <-- 圖標路徑
          disable_windowed_traceback=False,
          argv_emulation=False,
          target_arch=None,
          codesign_identity=None,
          entitlements_file=None,
      )
      
      coll = COLLECT(
          exe,
          a.binaries,
          a.zipfiles,
          a.datas,
          strip=False,
          upx=True,
          name='fastapi_app'
      )

      相當于之前在exe包中的a.binaries 和 a.datas從EXE 構造函數中移到了Collect的構造函數里面了。這樣會生成下面的目錄結構。

      image

      其中_internal目錄包含程序的相關依賴包和文件資源。

      image

      由于打包的.spec文件指定的目錄結構為松散結構(使用了COLLECT構造),那么可以看到 _internal / app目錄下有下面的目錄結構。

      image

      也就是我們前面通過 Analysis 模塊指定的datas集合路徑的內容。

       

      解決常見問題

        • 缺少依賴庫:如果打包后運行時出現缺少模塊的錯誤,可以嘗試將缺少的模塊加入到 hiddenimports 中,或者通過 --hidden-import 選項指定:

        • 大文件:如果使用 --onefile 時打包后的文件太大,考慮使用 --onedir 或通過壓縮文件等方法進行優化。
        • 處理資源文件:如果你的應用程序包含非 Python 代碼的資源(如圖像、配置文件、數據文件等),你需要通過 --add-data 選項指定資源文件的路徑,或者在 .spec 文件中修改 datas 選項。
        • 動態鏈接庫,如果你的應用程序依賴于特定的動態鏈接庫(如 DLL 文件或 .so 文件),你需要將這些庫包含到打包中。可以在 .spec 文件的 binaries 選項中指定:

        • 多平臺支持:PyInstaller 支持 Windows、Linux 和 macOS 等多個平臺,但需要在相應的平臺上打包。例如,如果你要為 Windows 用戶創建可執行文件,最好在 Windows 上運行 PyInstaller 來生成 Windows 的 .exe 文件。如果在 macOS 上打包,生成的文件只能在 macOS 上運行。

      在使用 PyInstaller 打包 FastAPI(或其他 Python 應用)時,兩個最常見、最容易混淆的參數就是:

      • --add-data(或 .spec 文件中的 datas

      • --hidden-import(或 .spec 文件中的 hiddenimports

      當 PyInstaller 打包時,它默認只會分析 Python 代碼的依賴模塊,而不會自動包含圖片、HTML 模板、配置文件等靜態資源。
      這時,就需要用 --add-data(或在 .spec 文件的 datas 中定義)告訴它要額外打包哪些文件或目錄。

      這里不介紹命令行的方式,只介紹.spec 文件寫法:

      image

      PyInstaller 在打包時,會分析你的 Python 源代碼(AST)來判斷使用了哪些模塊。但有些模塊是動態導入的(例如通過 importlib 或字符串導入),它就可能漏掉。

      解決辦法:用 --hidden-import 或者.spec文件中指定 hiddenimports 集合,告訴 PyInstaller 把這些模塊也打包進去,如上所示。

      總結起來就是:

      • datas = “我還有額外的文件要帶上”。

      • hiddenimports = “我還有額外的模塊要帶上”。

       

      五、FastAPI 項目打包的處理

      前面介紹了一個簡單的fastapi的項目結構和啟動,一般我們在開發的時候,啟動fastapi,直接調用python解析器運行main.py文件即可啟動,常規來說,main.py的啟動部分函數代碼如下。

      if __name__ == "__main__":
      
          # 日志配置路徑
          config_path = resource_path("app/uvicorn_config.json")
          # 運行 uvicorn
          try:
              config = uvicorn.Config(
                  app = socket_app,
                  reload=True,
                  host=settings.SERVER_IP,
                  log_config = config_config,  # 日志配置
              )
              server = uvicorn.Server(config)
              server.run()
          except Exception as e:
              raise e

      上面就是我實際項目簡化版本的main.py函數的啟動內容,正常開發環境,測試是正常的。但是通過pyinstall打包完成,并運行fastapi_app.exe的時候,提示找不到配置文件uvicorn_config.json。

      FileNotFoundError: [Errno 2] No such file or directory: 'app/uvicorn_config.json'

      這個原因是打包后執行exe文件的當前路徑改變了,打包進去的 exe 并沒有找到這個文件。首先:修改 .spec 文件,確保文件被打包進去,在 datas 里加這一行 ??
      (假設文件路徑是 app/uvicorn_config.json

      datas = [
          ("app/uvicorn_config.json", "app"),
          ("app/templates/*", "app/templates"),
          ("app/static/*", "app/static"),
          ("app/images/*", "app/images"),
      ]

      這一步確保 exe 中確實包含了你的 uvicorn_config.json 文件。

      其次:在 main.py 中使用通用的路徑函數resource_path:

      def resource_path(relative_path: str) -> str:
          """
          獲取資源文件真實路徑,支持:
          - 開發模式
          - PyInstaller onefile 模式
          - PyInstaller COLLECT (_internal) 模式
          """
      
          if hasattr(sys, '_MEIPASS'): # onefile 模式
              # exe 解壓臨時目錄
              base_path = sys._MEIPASS
          else:
              # 在松散模式下,_internal 目錄才是真正的數據存放處
              base_path = os.path.dirname(sys.executable) # exe 所在目錄
              internal_path = os.path.join(base_path, "_internal")
              if os.path.exists(internal_path):# 松散打包目錄
                  base_path = internal_path
              else:
                  # 直接開發運行時
                  base_path = os.path.abspath(".")
          return os.path.join(base_path, relative_path)

      然后修改你的 uvicorn.Config 代碼

      替換硬編碼路徑為:

      import uvicorn
      
      config_path = resource_path("app/uvicorn_config.json")
      
      config = uvicorn.Config(
          app=socket_app,
          host=settings.SERVER_IP,
          port=settings.SERVER_PORT,
          log_config=config_path,  # ? 動態獲取正確路徑
      )
      
      server = uvicorn.Server(config)
      server.run()

      上面啟動后,fastapi 配置文件定位到了,但是可能還會產生新的問題

      你可能會發現 app/uvicorn_config.json 里面配置的日志文件路徑和實際不對。

      image

      FileNotFoundError: [Errno 2] No such file or directory: '.../app/logs/log.log'

      Uvicorn 在加載 uvicorn_config.json 時的日志路徑是 相對進程工作目錄
      而不是相對 uvicorn_config.json 文件本身的路徑 ——
      這正是為什么你配置 "filename": "app/logs/log.log" 仍然報錯的根本原因。

      我們需要,在運行前動態修正 log_config.json 內部的路徑

      我們在加載 JSON 后,動態修改其中 "filename" 字段的路徑為打包后正確的絕對路徑。

      修正代碼后如下所示。

      if __name__ == "__main__":
      
          # 動態解析日志配置路徑
          config_path = resource_path("app/uvicorn_config.json")
          
          # 加載并修改日志配置,主要對日志文件路徑進行修正
          with open(config_path, "r", encoding="utf-8") as f:
              log_config = json.load(f)
      
          # 找到其中的 file handler,改寫 filename 為絕對路徑
          for handler in log_config.get("handlers", {}).values():
              if "filename" in handler:
                  log_file = handler["filename"]
                  abs_log_path = resource_path(log_file)
                  os.makedirs(os.path.dirname(abs_log_path), exist_ok=True)
                  handler["filename"] = abs_log_path  # 替換為絕對路徑
      
          # 運行 uvicorn(傳入已修改的 log_config dict)
          try:
              config = uvicorn.Config(
                  app = socket_app,
                  reload=True,
                  host=settings.SERVER_IP,
                  log_config = log_config,  # 日志配置,修正方式見上
              )
      
              server = uvicorn.Server(config)
              server.run()
          except Exception as e:
              raise e

      至此,所有問題都順利解決,能夠正常運行起來了,我們來看看FastAPI順利啟動后的效果。復制松散文件夾到服務器上雙擊運行即可,需要也可以修改配置文件.env實現相關修改。

      image

       

      ? 如果運行打包的exe 提示Missing command. 

      其實是 uvicorn 的提示,不是 PyInstaller 本身的報錯。可能是你的app設置上的問題,你在 main.py 里可能用了這種啟動方式:

      uvicorn.run("app.main:app", host="0.0.0.0", port=8000)

      解決方法 改成直接傳入 app 對象,而不是字符串路徑:

          # ? 改成直接傳 app 對象
          uvicorn.run(app, host="0.0.0.0", port=8000)

      這樣 uvicorn 就不會去找字符串形式的 module:app,而是直接運行你傳進去的 FastAPI 實例。 打包后的 exe 就能正常運行。

       

      ? 如果提示No module named 'aiomysql'

      這個問題其實是 PyInstaller 沒有把 aiomysql 打包進去,因為它是動態導入的,PyInstaller 靜態分析不到。

      方法 A:命令行添加 hidden-import

      pyinstaller --onefile --name fastapi_app --hidden-import aiomysql app/main.py

      方法 B:在 .spec 文件里加 hiddenimports

      找到 .spec 文件里的 Analysis,改成:

      a = Analysis(
          ['app/main.py'],
          pathex=[],
          binaries=[],
          datas=datas,
          hiddenimports=[
              "uvicorn",
              "fastapi",
              "pydantic",
              "aiomysql"   # ?? 加上這里
          ],
          hookspath=[],
          runtime_hooks=[],
          excludes=[],
          win_no_prefer_redirects=False,
          win_private_assemblies=False,
          cipher=block_cipher,
          noarchive=False,
      )

      FastAPI + 數據庫常用依賴很多(如 sqlalchemy[asyncio]asyncpgaiomysql 等),有些也可能被漏掉。做法同樣:把缺失的庫加到 hidden-import

      hiddenimports=[
          "uvicorn",
          "fastapi",
          "pydantic",
          "aiomysql",
          "asyncpg",
          "sqlalchemy.ext.asyncio",
      ]

      ? Data內容的寫法

      ("app/images/*", "app/images")

      會把 app/images 下的所有文件 放到 exe 解壓后的目錄里,路徑是 app/images/...

      如果代碼里是這樣寫的:

      open("app/images/logo.png", "rb")

      就能找到,也就是始終保持相對目錄的正確性。

       

      posted on 2025-10-11 11:56  伍華聰  閱讀(652)  評論(2)    收藏  舉報

      導航

      主站蜘蛛池模板: 三上悠亚精品一区二区久久| 国产成人综合色在线观看网站| 久久一本人碰碰人碰| 亚洲av免费成人精品区| 久久永久视频| 久久天堂无码av网站| 香港经典a毛片免费观看播放| 色呦呦九九七七国产精品| 桂平市| 中文字幕国产精品第一页| 四虎国产精品永久在线| 日本中文字幕有码在线视频| 婷婷四虎东京热无码群交双飞视频 | 国内偷自第一区二区三区| 特级做a爰片毛片免费看无码| 久久精品国产99国产精品澳门| 国产999久久高清免费观看| 欧美肥老太牲交大战| 精品国产午夜福利在线观看 | 又大又粗又硬又爽黄毛少妇| 老熟妇仑乱换频一区二区| 亚洲精品无码你懂的网站| 日本在线 | 中文| a毛片免费在线观看| 日韩视频一区二区三区视频| 极品少妇被猛得白浆直流草莓视频| 国产av一区二区三区久久| 精品一区二区中文字幕| 香港日本三级亚洲三级| 综合成人亚洲网友偷自拍| 国产99在线 | 亚洲| 国产AV福利第一精品| 九色综合国产一区二区三区| 与子敌伦刺激对白播放| 成人爽a毛片免费| 久久精品国产清自在天天线| 云龙县| 亚洲精品自拍在线视频| 亚洲综合色区另类av| 国产乱子伦视频在线播放 | 国产95在线 | 欧美|