10.13. การอัปโหลดเฟรมที่ถูกทริกเกอร์ไปยังคลาวด์¶
เมื่อมีการเคลื่อนไหว กล้องจะแสดงผลบนแดชบอร์ดทันที ซึ่งเพียงพอสำหรับการใช้งานแบบเรียลไทม์ แต่เจ้าของยังต้องการเก็บถาวรทุกเฟรมที่ถูกทริกเกอร์ไว้ถาวร โดยจัดเก็บไว้นอกตัวกล้อง ซึ่งต้องใช้การเรียก HTTP แบบส่งออก — กล้องทำหน้าที่เป็น client
10.13.1. กล้องในฐานะ client¶
โมดูล requests คือ HTTP client สำหรับการส่งออกของกล้อง โดยมี API ที่ออกแบบให้เหมือนกับ requests ของ CPython — ฟังก์ชันในโมดูลที่ตั้งชื่อตาม HTTP verb, อาร์กิวเมนต์คีย์เวิร์ด files=, json=, headers=, auth= ที่เหมือนกันทุกประการ หากคุณเคยเรียก HTTP จาก CPython มาก่อน คุณก็รู้จัก API นี้อยู่แล้ว:
import requests
import io
ARCHIVE_URL = 'https://api.backyard-cloud.com/frames'
ARCHIVE_TOKEN = load_archive_token()
async def archive_frame(jpeg, ts):
try:
r = requests.post(
ARCHIVE_URL,
files={'image': (
'frame-{}.jpg'.format(ts),
io.BytesIO(jpeg),
)},
headers={'Authorization': 'Bearer ' + ARCHIVE_TOKEN},
)
except OSError as e:
print('upload failed:', e)
return False
if r.status_code >= 400:
print('archive rejected:', r.status_code, r.reason)
return False
return True
requests.post() เปิดการเชื่อมต่อ TCP ส่งคำขอ และคืนค่า Response พร้อมด้วย status_code, reason, headers, content, json() และคุณสมบัติที่คุ้นเคยอื่นๆ
files={...} สร้าง body แบบ multipart/form-data โดย value คือ tuple แบบ (filename, file-like) ซึ่ง requests.post() จะอ่านข้อมูลจาก file-like ทีละส่วน เพื่อไม่ต้องบัฟเฟอร์ JPEG ทั้งหมดเป็น string ก่อน io.BytesIO ครอบข้อมูล JPEG ที่อยู่ในหน่วยความจำแล้ว เพื่อให้เปิดเป็น interface แบบ file ได้
headers={...} คือ dict ธรรมดาที่ส่งเป็น request header — ในที่นี้คือ bearer token ในตำแหน่ง Authorization มาตรฐาน ผู้ให้บริการเก็บถาวรจะระบุรูปแบบ token ที่ต้องการ ตัวอย่างนี้เป็นรูปแบบที่พบบ่อยที่สุด
10.13.2. เชื่อมต่อกับระบบตรวจจับการเคลื่อนไหว¶
coroutine ของระบบตรวจจับการเคลื่อนไหวที่แนะนำไว้ก่อนหน้านี้ทำงานทุกเฟรมใหม่ และทริกเกอร์เมื่อ change > state['threshold'] ให้เพิ่มการอัปโหลดตรงนั้น แต่เรียกใช้เป็น background task เพื่อไม่ให้ระบบตรวจจับหยุดเฝ้าดูระหว่างที่กำลังอัปโหลด:
async def motion_detector():
global last_motion
prev = None
while True:
await new_frame.wait()
change = compute_change(prev, latest_jpeg)
if change > state['threshold']:
state['trigger_count'] += 1
ts = int(time.time())
last_motion = {'ts': ts,
'count': state['trigger_count'],
'change': change}
motion_event.set()
asyncio.create_task(archive_frame(latest_jpeg, ts))
prev = latest_jpeg
await asyncio.sleep_ms(50)
asyncio.create_task() กำหนดเวลา coroutine การอัปโหลดและกลับมาทันที ระบบตรวจจับยังคงดึงเฟรมต่อไป การอัปโหลดทำงานควบคู่กัน กล้องไม่ค้างเลย
10.13.3. รูปแบบความล้มเหลว¶
โค้ดเครือข่ายอาจล้มเหลวได้ กล้องอาจออฟไลน์ ระบบเก็บถาวรอาจล่ม bearer token อาจหมดอายุ ประเภทข้อผิดพลาดที่ควรจับ:
OSError— ไม่สามารถเปิดการเชื่อมต่อ TCP หรือถูกตัดระหว่างการถ่ายโอน เช่น DNS ล้มเหลว ไม่มีเส้นทาง หรือ connection reset โดยrequestsจะ raise exception นี้โดยตรงstatus_code >= 400— เซิร์ฟเวอร์รับคำขอแล้วแต่ปฏิเสธ เช่น 401 สำหรับ token หมดอายุ, 403 สำหรับ token ที่ถูกเพิกถอน, 413 สำหรับ body ที่ใหญ่เกินไป, 5xx สำหรับระบบเก็บถาวรที่ขัดข้องSilent timeout —
requestsใช้ socket timeout ค่าเริ่มต้น (สองสามวินาที) เมื่อเกินจะ raiseOSErrorพร้อมกับerrno.ETIMEDOUT
สำหรับระบบเก็บถาวรที่สำคัญจริงๆ คุณควรจัดคิวเฟรมที่ถูกปฏิเสธไปที่ /sdcard/pending/ และลองใหม่ในลูปที่ช้ากว่า — ซึ่งต้องเพิ่มโค้ดอีกสองสามบรรทัดต่อกรณี นอกเหนือจากที่แสดงไว้
10.13.4. สิ่งที่ requests ไม่รองรับ¶
MicroPython port ถูกออกแบบให้เล็กตั้งใจ สิ่งที่ requests ของ CPython ทำได้แต่ตัวนี้ไม่รองรับ:
Connection pooling — ทุกการเรียกจะเปิดการเชื่อมต่อ TCP ใหม่
การลองใหม่อัตโนมัติเมื่อเกิดข้อผิดพลาดชั่วคราว ให้ครอบการเรียกด้วยตนเอง
Streaming responses —
r.contentถูกอ่านลง RAM ทั้งหมด ไม่มีตัวเลือกstream=Trueการแตกไฟล์ gzip อัตโนมัติสำหรับ response ที่บีบอัด ให้ตั้งค่า header
Accept-Encodingอย่างชัดเจนเฉพาะเมื่อเซิร์ฟเวอร์รองรับเท่านั้น
ดู requests --- HTTP client สำหรับรายการเมธอดทั้งหมดและขอบเขตการรองรับ
HTTPS ทำงานได้ทันที — URL scheme เป็นตัวกำหนด และ SSL context ค่าเริ่มต้นจะถูกสร้างขึ้นทันที หากต้องการตรวจสอบใบรับรองของระบบเก็บถาวรกับ CA bundle ที่โหลดไว้บนกล้อง ดูส่วน as a client ใน การตรวจสอบเซิร์ฟเวอร์สาธารณะ (กล้องทำหน้าที่เป็น client)
แอปพลิเคชันพร้อมใช้งานสมบูรณ์แล้ว: ดูตัวอย่างแบบสด ตรวจจับการเคลื่อนไหว แดชบอร์ดพร้อมระบบล็อกอิน HTTPS, CORS/CSRF และระบบเก็บถาวรบนคลาวด์