FastAPI 支持文件下載和上傳

文章目錄

  • 1. 文件下載處理
    • 1.1. 服務端處理
      • 1.1.1. 下載小文件
      • 1.1.2. 下載大文件(yield 支持預覽的)
      • 1.1.3. 下載大文件(bytes)
      • 1.1.4. 提供靜態文件服務
      • 1.1.5. 中文文件名錯誤
    • 1.2. 客戶端處理
      • 1.2.1. 普通下載
      • 1.2.2. 分塊下載
      • 1.2.3. 顯示進度條下載
      • 1.2.4. 帶有斷點續傳的下載
      • 1.2.5. 帶有超時和重試的下載
      • 1.2.6. 完整的下載器實現
  • 2. 文件上傳處理
    • 2.1. 服務端處理
      • 2.1.1. 上傳小文件
      • 2.1.2. 上傳大文件
    • 2.2. 客戶端處理

參考:
https://blog.csdn.net/weixin_42502089/article/details/147689236
https://www.cnblogs.com/bitterteaer/p/17581746.html
修改下載緩沖區大小
https://ask.csdn.net/questions/8328950

1. 文件下載處理

對于文件下載,FastAPI 提供了 FileResponse 和 StreamingResponse 兩種方式。 FileResponse 適合小文件,而 StreamingResponse 適合大文件,因為它可以分塊返回文件內容。

1.1. 服務端處理

1.1.1. 下載小文件

使用 FileResponse 可以直接下載文件,而無需在內存中加載整個文件。

"""
fastapi + request 上傳和下載功能
"""
from fastapi import FastAPI, UploadFile
from fastapi.responses import FileResponse
import uvicornapp = FastAPI()# filename 下載時設置的文件名
@app.get("/download/small/{filename}")
async def download_small_file(filename: str):print(filename)file_path = "./測試任務.pdf"return FileResponse(file_path, filename=filename, media_type="application/octet-stream")if __name__ == '__main__':uvicorn.run(app, port=8000)

保證當前目錄下有名為“測試任務.pdf”的文件。
然后使用瀏覽器下載:
http://127.0.0.1:8000/download/small/ceshi.pdf
在這里插入圖片描述

1.1.2. 下載大文件(yield 支持預覽的)

使用 StreamingResponse 可以分塊下載文件,這樣不會占用太多服務器資源,特別適用于大文件的下載。

from fastapi.responses import StreamingResponse
from fastapi import HTTPException
@app.get("/download/big/{filename}")
async def download_big_file(filename: str):def iter_file(path: str):with open(file=path, mode="rb") as tfile:yield tfile.read()# while chunk := tfile.read(1024*1024):  # 1MB 緩沖區#     yield chunkfile_path = "./測試任務.pdf"if not os.path.exists(file_path):raise HTTPException(status_code=404, detail="File not found")# # 支持瀏覽器預覽# return StreamingResponse(content=iter_file(path=file_path), status_code = 200,)# 直接下載return StreamingResponse(iter_file(path=file_path), media_type="application/octet-stream", headers={"Content-Disposition": f"attachment; filename={filename}"})

然后使用瀏覽器下載:
http://127.0.0.1:8000/download/big/ceshi_big.pdf

1.1.3. 下載大文件(bytes)

import io
@app.get("/download/bytes/{filename}")
async def download_bytes_file(filename: str):def read_bytes(path: str):content = "Error"with open(file=path, mode="rb") as tfile:content = tfile.read()# # 失敗,需要轉成bytes輸出# return contentreturn io.BytesIO(content)file_path = "./測試任務.pdf"if not os.path.exists(file_path):raise HTTPException(status_code=404, detail="File not found")# 解決中文名錯誤from urllib.parse import quote# return StreamingResponse(content=read_bytes(path=file_path), media_type="application/octet-stream", #                          headers={"Content-Disposition": "attachment; filename={}".format(quote(filename))})return StreamingResponse(content=read_bytes(path=file_path), media_type="application/octet-stream", headers={"Content-Disposition": "attachment; filename={}".format(quote(filename))})

1.1.4. 提供靜態文件服務

FastAPI 允許開發者使用 StaticFiles 來提供靜態文件服務。這類似于傳統 Web 服務器處理文件的方式。

from fastapi.staticfiles import StaticFiles# app.mount("/static", StaticFiles(directory="static", html=True), name="free")
app.mount("/static", StaticFiles(directory="fonts", html=True), name="free")

尚未測試通過。

1.1.5. 中文文件名錯誤

下載文件時,當傳遞文件名為中文時,報錯。
在這里插入圖片描述

    # 解決中文名錯誤from urllib.parse import quotereturn StreamingResponse(iter_file(path=file_path), media_type="application/octet-stream", headers={"Content-Disposition": "attachment; filename={}".format(quote(filename))})

在這里插入圖片描述

1.2. 客戶端處理

參考(還有進度條, 帶有斷點續傳的下載, 帶有超時和重試的下載):
https://blog.csdn.net/u013762572/article/details/145158401
批量上傳下載
https://blog.csdn.net/weixin_43413871/article/details/137027968

1.2.1. 普通下載

import requests
import os"""方式1,將整個文件下載在保存到本地"""
def download_file_bytes(file_name):# 以下三個地址均可以url = "http://127.0.0.1:8000/download/small/ceshi_samll.pdf"url = "http://127.0.0.1:8000/download/bytes/ceshi_bytes.pdf"url = "http://127.0.0.1:8000/download/big/ceshi_big.pdf"# response = requests.get(url, params={"filename": "1.txt"})response = requests.get(url)# print(response.text)with open(file_name, 'wb') as file:# file.write(response.text)file.write(response.content)if __name__ == '__main__':download_file_bytes("本地測試下載文件bytes.pdf")

1.2.2. 分塊下載

import requests
import os"""方式2,通過流的方式一次寫入8192字節"""
def download_file_big(file_name):# 以下三個地址均可以url = "http://127.0.0.1:8000/download/small/ceshi_samll.pdf"# url = "http://127.0.0.1:8000/download/big/ceshi_big.pdf"# url = "http://127.0.0.1:8000/download/bytes/ceshi_bytes.pdf"# response = requests.get(url, params={"filename": "./測試任務.pdf"}, stream=True)response = requests.get(url, stream=True)with open(file_name, 'wb') as file:for chunk in response.iter_content(chunk_size=8192):file.write(chunk)if __name__ == '__main__':download_file_big("本地測試下載文件big.pdf")

1.2.3. 顯示進度條下載

import requests
import os
from tqdm import tqdmdef download_file_tqdm(file_name):# 以下三個地址均可以# url = "http://127.0.0.1:8000/download/small/ceshi_samll.pdf"# url = "http://127.0.0.1:8000/download/big/ceshi_big.pdf"url = "http://127.0.0.1:8000/download/bytes/ceshi_bytes.pdf"response = requests.get(url, stream=True)if response.status_code == 200:file_size = int(response.headers.get('content-length', 0))# 顯示進度條progress = tqdm(response.iter_content(chunk_size=8192), total=file_size,unit='B', unit_scale=True)with open(file_name, 'wb') as f:for data in progress:f.write(data)return Truereturn Falseif __name__ == '__main__':download_file_tqdm("本地測試下載文件tqdm.pdf")

運行結果:

> python.exe .\fast_client.py
1.92kB [00:00, 14.0kB/s]

1.2.4. 帶有斷點續傳的下載

# 帶有斷點續傳的下載
def resume_download(file_name):# 以下三個地址均可以# url = "http://127.0.0.1:8000/download/small/ceshi_samll.pdf"# url = "http://127.0.0.1:8000/download/big/ceshi_big.pdf"url = "http://127.0.0.1:8000/download/bytes/ceshi_bytes.pdf"# 獲取已下載文件大小initial_pos = os.path.getsize(file_name) if os.path.exists(file_name) else 0# 設置 Headerheaders = {'Range': f'bytes={initial_pos}-'}response = requests.get(url, stream=True, headers=headers)# 追加模式打開文件mode = 'ab' if initial_pos > 0 else 'wb'with open(file_name, mode) as f:for chunk in response.iter_content(chunk_size=8192):if chunk:f.write(chunk)

尚未測試

1.2.5. 帶有超時和重試的下載

# 帶有超時和重試的下載
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import time
def download_with_retry(file_name, max_retries=3, timeout=30):# 以下三個地址均可以# url = "http://127.0.0.1:8000/download/small/ceshi_samll.pdf"# url = "http://127.0.0.1:8000/download/big/ceshi_big.pdf"url = "http://127.0.0.1:8000/download/bytes/ceshi_bytes.pdf"session = requests.Session()# 設置重試策略retries = Retry(total=max_retries,backoff_factor=1,status_forcelist=[500, 502, 503, 504])session.mount('http://', HTTPAdapter(max_retries=retries))session.mount('https://', HTTPAdapter(max_retries=retries))try:response = session.get(url, stream=True, timeout=timeout)with open(file_name, 'wb') as f:for chunk in response.iter_content(chunk_size=8192):if chunk:f.write(chunk)return Trueexcept Exception as e:print(f"Download failed: {str(e)}")return False

尚未測試

1.2.6. 完整的下載器實現

import requests
from tqdm import tqdm
import os
from pathlib import Path
import hashlibclass FileDownloader:def __init__(self, chunk_size=8192):self.chunk_size = chunk_sizeself.session = requests.Session()def get_file_size(self, url):response = self.session.head(url)return int(response.headers.get('content-length', 0))def get_file_hash(self, file_path):sha256_hash = hashlib.sha256()with open(file_path, "rb") as f:for byte_block in iter(lambda: f.read(4096), b""):sha256_hash.update(byte_block)return sha256_hash.hexdigest()def download(self, url, save_path, verify_hash=None):save_path = Path(save_path)# 創建目錄save_path.parent.mkdir(parents=True, exist_ok=True)# 獲取文件大小file_size = self.get_file_size(url)# 設置進度條progress = tqdm(total=file_size,unit='B',unit_scale=True,desc=save_path.name)try:response = self.session.get(url, stream=True)with save_path.open('wb') as f:for chunk in response.iter_content(chunk_size=self.chunk_size):if chunk:f.write(chunk)progress.update(len(chunk))progress.close()# 驗證文件完整性if verify_hash:downloaded_hash = self.get_file_hash(save_path)if downloaded_hash != verify_hash:raise ValueError("File hash verification failed")return Trueexcept Exception as e:progress.close()print(f"Download failed: {str(e)}")if save_path.exists():save_path.unlink()return Falsedef download_multiple(self, url_list, save_dir):results = []for url in url_list:filename = url.split('/')[-1]save_path = Path(save_dir) / filenamesuccess = self.download(url, save_path)results.append({'url': url,'success': success,'save_path': str(save_path)})return results# 使用示例
downloader = FileDownloader()# 單文件下載
url = "http://127.0.0.1:8000/download/bytes/ceshi_bytes.pdf"
downloader.download(url, save_path="downloads/file.pdf")# # 多文件下載
# urls = [
#     "https://example.com/file1.pdf",
#     "https://example.com/file2.pdf"
# ]
# results = downloader.download_multiple(urls, "downloads")

運行結果:

> python.exe .\fast_client_plus.py
file.pdf: 9.18MB [00:00, 60.2MB/s]

2. 文件上傳處理

FastAPI 提供了 File() 和 UploadFile() 兩個類用于處理文件上傳。 File() 適用于小文件,而 UploadFile() 則更適合處理大文件。在文件上傳時,我們建議使用異步版本的函數,這樣可以避免阻塞服務器。

2.1. 服務端處理

2.1.1. 上傳小文件

使用 File() 時,可以通過 httpie 或 requests 庫來模擬上傳操作。需要注意的是,上傳文件時應該使用表單而不是 JSON,這可以通過在命令中加入 -f 或 --form 參數來指定。

from fastapi import File
@app.post("/upload/small")
async def upload_small_file(small_file: bytes = File()) -> str:return f"file size: {len(small_file)}"

尚未測試

2.1.2. 上傳大文件

對于大文件,建議使用 UploadFile ,因為它會在服務器的磁盤上創建一個臨時文件對象,而不是將整個文件加載到內存中。這樣可以有效避免服務器內存溢出。

from fastapi import UploadFile
import time
import os
@app.post("/upload/big")
async def upload_big_file(big_file: UploadFile= File(...)):filename = f"{str(time.time()).replace('.','')}-{big_file.filename}"path = os.path.join("upload", filename)with open(path, "wb") as f:f.write(big_file.file.read())f.flush()return {"filename": filename,"filesize": big_file.size}

2.2. 客戶端處理

import requests
import osdef upload_file_big(file_path):url = "http://127.0.0.1:8000/upload/big"with open(file_path, 'rb') as f:contents = f.read()response = requests.post(url, files={"file": (os.path.basename(file_path), contents, 'multipart/form-data')})return response.json()if __name__ == '__main__':upload_file_big(r"./example.pdf")

尚未測試

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81879.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81879.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81879.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

naive-ui切換主題

1、在App.vue文件中使用 <script setup lang"ts"> import Dashboard from ./views/dashboard/index.vue import { NConfigProvider, NGlobalStyle, darkTheme } from naive-ui import { useThemeStore } from "./store/theme"; // 獲取存儲的主題類…

Kotlin 協程 (三)

協程通信是協程之間進行數據交換和同步的關鍵機制。Kotlin 協程提供了多種通信方式&#xff0c;使得協程能夠高效、安全地進行交互。以下是對協程通信的詳細講解&#xff0c;包括常見的通信原語、使用場景和示例代碼。 1.1 Channel 定義&#xff1a;Channel 是一個消息隊列&a…

使用SQLite Studio導出/導入SQL修復損壞的數據庫

使用SQLite Studio導出/導入SQL修復損壞的數據庫 使用Zotero時遇到了數據庫損壞&#xff0c;在軟件中寸步難行&#xff0c;遂嘗試修復數據庫。 一、SQLite Studio簡介 SQLite Studio是一款專為SQLite數據庫設計的免費開源工具&#xff0c;支持Windows/macOS/Linux。相較于其…

【git config --global alias | Git分支操作效率提升實踐指南】

git config --global alias | Git分支操作效率提升實踐指南 背景與痛點分析 在現代軟件開發團隊中&#xff0c;Git分支管理是日常工作的重要組成部分。特別是在規范的開發流程中&#xff0c;我們經常會遇到類似 feature/user-management、bugfix/login-issue 或 per/cny/dev …

(八)深度學習---計算機視覺基礎

分類問題回歸問題聚類問題各種復雜問題決策樹√線性回歸√K-means√神經網絡√邏輯回歸√嶺回歸密度聚類深度學習√集成學習√Lasso回歸譜聚類條件隨機場貝葉斯層次聚類隱馬爾可夫模型支持向量機高斯混合聚類LDA主題模型 一.圖像數字化表示及建模基礎 二.卷積神經網絡CNN基本原…

在tensorflow源碼環境里,編譯出獨立的jni.so,避免依賴libtensorflowlite.so,從而實現apk體積最小化

需要在APP里使用tensorflow lite來運行PC端訓練的model.tlite&#xff0c;又想apk的體積最小&#xff0c;嘗試了如下方法&#xff1a; 1. 在gradle里配置 implementation("org.tensorflow:tensorflow-lite:2.16.1") 這樣會引入tensorflow.jar&#xff0c;最終apk的…

neo4j框架:java安裝教程

安裝使用neo4j需要事先安裝好java&#xff0c;java版本的選擇是一個犯難的問題。本文總結了在安裝java和使用Java過程中遇到的問題以及相應的解決方法。 Java的安裝包可以在java官方網站Java Downloads | Oracle 中國進行下載 以java 8為例&#xff0c;選擇最后一行的x64 compr…

[服務器備份教程] Rclone實戰:自動備份數據到阿里云OSS/騰訊云COS等對象存儲

更多服務器知識&#xff0c;盡在hostol.com 各位服務器的守護者們&#xff0c;咱們都知道&#xff0c;數據是數字時代的“黃金”&#xff0c;而服務器上的數據更是我們業務的命脈。可天有不測風云&#xff0c;硬盤可能會突然“壽終正寢”&#xff0c;手滑執行了“毀滅性”命令…

Nextjs App Router 開發指南

Next.js是一個用于構建全棧web應用的React框架。App Router 是 nextjs 的基于文件系統的路由器&#xff0c;它使用了React的最新特性&#xff0c;比如 Server Components, Suspense, 和 Server Functions。 術語 樹(Tree): 一種用于可視化的層次結構。例如&#xff0c;包含父…

山東大學計算機圖形學期末復習15——CG15

CG15 OpenGL緩沖區、讀寫操作以及混合&#xff08;Blending&#xff09; 一、OpenGL緩沖區概述 OpenGL中的緩沖區是用于存儲像素數據的內存區域&#xff0c;主要包括以下類型&#xff1a; 顏色緩沖區&#xff08;Color Buffer&#xff09;&#xff1a;存儲每個像素的顏色值…

html+css+js趣味小游戲~記憶卡片配對(附源碼)

下面是一個簡單的記憶卡片配對游戲的完整代碼&#xff0c;使用HTML、CSS和JavaScript實現&#xff1a; html <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"wid…

?個并發訪問量?較?的key在某個時間過期,在redis中這個時間過期什么意思

在 Redis 中&#xff0c;當提到一個鍵&#xff08;key&#xff09;“在這個時間過期”&#xff0c;指的是為該鍵設置了生存時間&#xff08;TTL, Time To Live&#xff09;或過期時間&#xff08;expiration time&#xff09;。一旦到達設定的過期時間&#xff0c;Redis 會自動…

【設計模式】- 行為型模式1

模板方法模式 定義了一個操作中的算法骨架&#xff0c;將算法的一些步驟推遲到子類&#xff0c;使得子類可以不改變該算法結構的情況下重定義該算法的某些步驟 【主要角色】&#xff1a; 抽象類&#xff1a;給出一個算法的輪廓和骨架&#xff08;包括一個模板方法 和 若干基…

ubuntu22.04 卸載ESP-IDF

要在Ubuntu 22.04上完全卸載ESP-IDF&#xff0c;請按照以下步驟操作&#xff1a; 卸載ESP-IDF的步驟 刪除ESP-IDF目錄&#xff1a; # 假設ESP-IDF安裝在~/esp/esp-idf目錄 rm -rf ~/esp/esp-idf刪除ESP-IDF工具鏈和下載的工具&#xff1a; rm -rf ~/.espressif從PATH中移除ESP…

SQLMesh 內置宏詳解:@PIVOT等常用宏的核心用法與示例

本文系統解析 SQLMesh 的四個核心內置宏&#xff0c;涵蓋行列轉換的 PIVOT、精準去重的 DEDUPLICATE、靈活生成日期范圍的 DATE_SPINE&#xff0c;以及動態表路徑解析的 RESOLVE_TEMPLATE。通過真實案例演示參數配置與 SQL 渲染邏輯&#xff0c;并對比宏調用與傳統 SQL 的差異&…

基于Springboot + vue3實現的工商局商家管理系統

項目描述 本系統包含管理員、商家兩個角色。 管理員角色&#xff1a; 用戶管理&#xff1a;管理系統中所有用戶的信息&#xff0c;包括添加、刪除和修改用戶。 許可證申請管理&#xff1a;管理商家的許可證申請&#xff0c;包括搜索、修改或刪除許可證申請。 許可證審批管理…

第五部分:第五節 - Express 路由與中間件進階:廚房的分工與異常處理

隨著你的 Express 應用變得越來越大&#xff0c;所有的路由和中間件都寫在一個文件里會變得難以管理。這時候就需要將代碼進行拆分和組織。此外&#xff0c;一個健壯的后端應用必須能夠優雅地處理錯誤和一些常見的 Web 開發問題&#xff0c;比如跨域。 路由模塊化 (express.Ro…

萌新聯賽第(三)場

C題 這道題用暴力去寫想都不要想&#xff0c;一定超時&#xff0c;于是我們需要優化&#xff0c;下面是思路過程&#xff1a; 如圖&#xff0c;本題只需找到x的因數個數和(n-x)的因數個數&#xff0c;這兩個相乘&#xff0c;得到的就是對于這個x來說組合的個數&#xff0c;且x…

【Android構建系統】如何在Camera Hal的Android.bp中選擇性引用某個模塊

背景描述 本篇文章是一個Android.bp中選擇性引用某個模塊的實例。 如果是Android.mk編譯時期&#xff0c;在編譯階段通過某個條件判斷是不是引用某個模塊A, 是比較好實現的。Android15使用Android.bp構建后&#xff0c;要想在Android.bp中通過自定義的一個變量或者條件實現選…

【OneNET】_01_使用微信小程序通過新版OneNET平臺獲取STM32設備信息并進行控制

【OneNET】_01_使用微信小程序通過新版OneNET平臺獲取STM32設備信息并進行控制 一、 前言1.1 OntNET硬件方面: STM32F103C8T6 ESP01S教程 1.2 微信小程序方面 二、STM32代碼部分修改三、微信小程序修改的部分四、小筆記&#xff08;個人雜記&#xff09;4.1 OneNETOneNET物聯網…