FastAPI 上傳檔案至 Azure Blob

使用 FastAPI 上傳檔案並儲存至 Azure Blob Storage 的過程,主要分為環境準備、撰寫 FastAPI 接收檔案的介面,以及呼叫 Azure SDK 進行上傳這三個步驟。

首先需要安裝必要的套件。透過 pip 安裝 fastapi, python-multipart 用於處理檔案上傳,以及 azure-storage-blob 作為與 Azure 溝通的工具。

在程式碼實作中,必須先建立 BlobServiceClient 連線。這通常需要從 Azure 入口網站取得儲存帳戶的連接字串。接著定義一個 FastAPI 的 POST 路由,並使用 UploadFile 類別來接收用戶端傳送的檔案。UploadFile 的優點在於它能處理大型檔案,而不會一次性將所有內容載入記憶體。

在路由函數內部,透過 blob_service_client.get_blob_client 指定容器名稱與儲存後的檔案名稱。使用 upload_blob 方法時,可以直接傳入 upload_file.file,因為這是一個類檔案物件。上傳完成後,建議使用 try-finally 結構確保檔案資源被正確關閉。

以下是基礎的實作範例:

Python

from fastapi import FastAPI, UploadFile, File
from azure.storage.blob import BlobServiceClient
import os

app = FastAPI()

# 建議將連線字串存放在環境變數中
AZURE_STORAGE_CONNECTION_STRING = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
CONTAINER_NAME = "your-container-name"

blob_service_client = BlobServiceClient.from_connection_string(AZURE_STORAGE_CONNECTION_STRING)

@app.post("/upload/")
async def upload_to_azure(file: UploadFile = File(...)):
    try:
        blob_client = blob_service_client.get_blob_client(container=CONTAINER_NAME, blob=file.filename)
        
        # 直接讀取檔案內容並上傳
        contents = await file.read()
        blob_client.upload_blob(contents, overwrite=True)
        
        return {"filename": file.filename, "status": "success"}
    except Exception as e:
        return {"error": str(e)}
    finally:
        await file.close()

儲存檔案時需要注意容器的權限設定。如果容器設定為私有,上傳後的檔案無法透過 URL 直接存取,必須透過程式碼產生具有時效性的 SAS Token 才能下載或檢視。另外,針對大型檔案上傳,可以考慮使用串流方式處理,避免 read 耗盡伺服器記憶體。


如何產生 SAS Token 以供使用者下載這些已上傳的檔案?

要產生 SAS Token 供使用者下載檔案,最常見的做法是生成一個具有時效性的 URL。這樣既能保持儲存帳戶的私密性,又能讓獲得該連結的使用者在限期內存取資源。

實作上會使用 azure-storage-blob 套件中的 generate_blob_sas 函數。開發者需要定義幾個關鍵參數:儲存帳戶名稱、帳戶金鑰、容器名稱、檔案路徑,以及該 Token 的權限與過期時間。通常權限會設定為 read,代表使用者只能下載而不能修改或刪除。

安全性是產生 SAS 的重點。建議將有效期限設定得盡可能短,例如 1 小時或更短。此外,建議透過環境變數管理儲存帳戶的金鑰,避免將敏感資訊直接寫在程式碼中。

以下是產生下載連結的實作方式:

Python

from datetime import datetime, timedelta, timezone
from azure.storage.blob import generate_blob_sas, BlobSasPermissions
import os

ACCOUNT_NAME = os.getenv("AZURE_STORAGE_ACCOUNT_NAME")
ACCOUNT_KEY = os.getenv("AZURE_STORAGE_ACCOUNT_KEY")
CONTAINER_NAME = "your-container-name"

def create_service_sas_blob(blob_name: str):
    # 設定過期時間為當下時間加上 1 小時
    expiry_time = datetime.now(timezone.utc) + timedelta(hours=1)
    
    sas_token = generate_blob_sas(
        account_name=ACCOUNT_NAME,
        container_name=CONTAINER_NAME,
        blob_name=blob_name,
        account_key=ACCOUNT_KEY,
        permission=BlobSasPermissions(read=True),
        expiry=expiry_time
    )
    
    # 組合完整的下載網址
    sas_url = f"https://{ACCOUNT_NAME}.blob.core.windows.net/{CONTAINER_NAME}/{blob_name}?{sas_token}"
    return sas_url

@app.get("/download-link/{filename}")
async def get_download_url(filename: str):
    url = create_service_sas_blob(filename)
    return {"download_url": url}

這段程式碼會回傳一個包含簽章的完整網址。使用者點擊該網址後,瀏覽器會直接從 Azure 下載檔案。要注意的是,產生的網址長度較長,且包含安全性參數,應確保在傳輸過程中使用 HTTPS 協定加密。


檔案需要供大量不特定對象長期下載,或是作為網站靜態資源使用,如何將容器權限調整為公開存取?

將 Azure Blob Storage 容器調整為公開存取,主要涉及兩個層級的設定:儲存帳戶層級的允許開關,以及個別容器層級的存取層級設定。這通常用於存放網站圖片、CSS 檔案或不需要驗證即可下載的公開文件。

第一步必須在 Azure 入口網站的儲存帳戶設定中,找到安全性與網路區塊下的設定選項,並確認允許 Blob 公用存取已經勾選。如果這個全域開關沒有開啟,即使個別容器設定為公開,外部依然無法存取。

第二步是針對特定容器進行設定。在容器列表中選擇目標容器,點選變更存取層級。這裡通常有三個選項:不公開、僅限 Blob 以及容器。若選擇僅限 Blob,使用者知道正確的 URL 就能讀取檔案,但無法列出容器內的所有檔案清單;若選擇容器,使用者則可以透過 API 列出該路徑下的所有檔案名稱。

使用 Python SDK 進行自動化調整的程式碼如下:

Python

from azure.storage.blob import BlobServiceClient, PublicAccess
import os

AZURE_STORAGE_CONNECTION_STRING = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
CONTAINER_NAME = "your-public-container"

blob_service_client = BlobServiceClient.from_connection_string(AZURE_STORAGE_CONNECTION_STRING)
container_client = blob_service_client.get_container_client(CONTAINER_NAME)

# 將容器存取層級設定為僅限 Blob 公開讀取
container_client.set_container_access_policy(public_access=PublicAccess.Blob)

# 之後檔案的存取路徑就不需要 SAS Token
public_url = f"https://{blob_service_client.account_name}.blob.core.windows.net/{CONTAINER_NAME}/your-file.png"
print(f"檔案公開連結:{public_url}")

一旦設定為公開,原本包含 SAS Token 的複雜網址就可以簡化為標準的 URL 格式。這種方式雖然方便,但也要注意流量成本,因為任何知道網址的人都能下載檔案。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *