aiohttp 非同步http請求-4.檔案上傳multipart/form-data
前言
檔案上傳介面,post 請求引數型別content-type:multipart/form-data,上傳檔案分2種情況
- 小檔案上傳,可以直接用open函式讀取
- 大檔案上傳,aiohttp支援多種型別的檔案以流媒體的形式上傳
官方文件示例
上傳 multipart 型別
url = 'http://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}
await session.post(url, data=files)
也可以明確設定filename
和 content_type
url = 'http://httpbin.org/post' data = FormData() data.add_field('file', open('report.xls', 'rb'), filename='report.xls', content_type='application/vnd.ms-excel') await session.post(url, data=data)
參考案例
用fiddler抓包,檢視抓到的介面,以下這種介面就是multipart/form-data
Content-Type: multipart/form-data
body引數是這種格式:
-----------------------------22165374713946
Content-Disposition: form-data; name="localUrl"
yoyoketang.png
-----------------------------22165374713946
Content-Disposition: form-data; name="imgFile"; filename="yoyoketang.png"
Content-Type: image/png
上面的介面需要傳2個引數
- title 傳字串
- file 傳一個檔案
官網文件寫的是隻傳一個file引數,實際驗證也可以傳其它字串引數,如下示例:
import aiohttp import asyncio async def main(): async with aiohttp.ClientSession('http://127.0.0.1:8000') as session: files = { 'file': open('a.jpg', 'rb'), 'title': '檔案上傳' } async with session.post('/api/v1/upfile/', data=files) as resp: print(resp.url) print(await resp.text()) loop = asyncio.get_event_loop() loop.run_until_complete(main())
執行結果
http://http://127.0.0.1:8000/api/v1/upfile/
{"code":0,"msg":"success!","data":{"file":"/media/up_image/a_TEn5GLR.jpg","title":"檔案上傳","timestamp":"2022-04-21 11:15:28"}}
使用 FormData 類
FormData 類自定義檔案型別和名稱
import aiohttp
import asyncio
from aiohttp import FormData
async def main():
async with aiohttp.ClientSession('http://49.235.92.12:7005') as session:
data = FormData()
data.add_field('file',
open('a.jpg', 'rb'),
filename='a.jpg',
content_type='image/png')
data.add_field('title', '檔案上傳1')
async with session.post('/api/v1/upfile/', data=data) as resp:
print(resp.url)
print(await resp.text())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
大檔案上傳
小檔案上傳可以直接open函式讀取,大檔案直接讀取會非常消耗內容。aiohttp支援多種型別的流式上傳,這使您可以傳送大檔案而無需將它們讀入記憶體。
作為一個簡單的案例,只需為您的 body 提供一個類似檔案的物件:
with open('massive-body', 'rb') as f:
await session.post('http://httpbin.org/post', data=f)
或者您可以使用非同步生成器:
async def file_sender(file_name=None):
async with aiofiles.open(file_name, 'rb') as f:
chunk = await f.read(64*1024)
while chunk:
yield chunk
chunk = await f.read(64*1024)
# Then you can use file_sender as a data provider:
async with session.post('http://httpbin.org/post',
data=file_sender(file_name='huge_file')) as resp:
print(await resp.text())
因為該 content 屬性是一個 StreamReader(提供非同步迭代器協議),所以您可以將 get 和 post 請求連結在一起:
resp = await session.get('http://python.org')
await session.post('http://httpbin.org/post',
data=resp.content)
筆記 Python 3.5 沒有對非同步生成器的原生支援,使用 async_generator庫作為解決方法。
3.1 版後已棄用:aiohttp仍支援aiohttp.streamer裝飾器,但不推薦使用此方法,而支援非同步生成器,如上所示。