refactor(edge): 截图不再保存本地,直接编码为base64上传COS

- ResultReporter: 截图通过 cv2.imencode 编码为 JPEG base64,
  直接放入 Redis 消息,不再调用 ImageStorageManager 保存本地文件
- AlarmUploadWorker: 从 Redis 读取 base64 解码为字节流,
  使用 put_object(Body=bytes) 直接上传 COS,移除 local: 回退逻辑
- 移除 AlarmInfo.snapshot_local_path,改为 snapshot_b64
- COS 未配置时返回 None 进入重试(不再静默回退本地路径)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 14:03:12 +08:00
parent d71d5da740
commit d9f78e0b48
2 changed files with 33 additions and 67 deletions

View File

@@ -15,7 +15,6 @@ Redis Key 设计:
import json
import logging
import os
import threading
import time
from datetime import datetime, timezone
@@ -183,21 +182,16 @@ class AlarmUploadWorker:
self._logger.info(f"开始处理告警: {alarm_id} (retry={retry_count})")
# Step 1: 上传截图到 COS
snapshot_local_path = alarm_data.get("snapshot_local_path")
# Step 1: 上传截图到 COS(从 base64 解码后直接上传字节流)
snapshot_b64 = alarm_data.get("snapshot_b64")
object_key = None
if snapshot_local_path:
# 截图是异步保存的,等待文件写入完成(最多 3 秒)
if not os.path.exists(snapshot_local_path):
for _ in range(6):
time.sleep(0.5)
if os.path.exists(snapshot_local_path):
break
if os.path.exists(snapshot_local_path):
if snapshot_b64:
try:
import base64
image_bytes = base64.b64decode(snapshot_b64)
object_key = self._upload_snapshot_to_cos(
snapshot_local_path,
image_bytes,
alarm_id,
alarm_data.get("device_id", "unknown"),
)
@@ -205,17 +199,12 @@ class AlarmUploadWorker:
# COS 上传失败,进入重试
self._handle_retry(alarm_json, "COS 上传失败")
return
elif object_key == "":
# COS 未配置,使用本地截图路径作为回退
captures_base = os.path.join("data", "captures")
rel_path = os.path.relpath(snapshot_local_path, captures_base)
rel_path = rel_path.replace("\\", "/")
object_key = f"local:{rel_path}"
self._logger.info(f"使用本地截图路径: {object_key}")
else:
self._logger.warning(f"截图文件不存在: {snapshot_local_path}")
except Exception as e:
self._logger.error(f"截图解码/上传失败: {e}")
self._handle_retry(alarm_json, f"截图处理失败: {e}")
return
# Step 2: HTTP 上报告警元数据
# Step 2: HTTP 上报告警元数据(不含截图二进制数据)
report_data = {
"alarm_id": alarm_data.get("alarm_id"),
"alarm_type": alarm_data.get("alarm_type"),
@@ -234,14 +223,6 @@ class AlarmUploadWorker:
if success:
self._stats["processed"] += 1
self._logger.info(f"告警上报成功: {alarm_id}")
# 仅在 COS 上传成功时删除本地截图;本地回退模式(local:)不删除
if snapshot_local_path and os.path.exists(snapshot_local_path) and object_key and not object_key.startswith("local:"):
try:
os.remove(snapshot_local_path)
self._logger.debug(f"已删除本地截图: {snapshot_local_path}")
except Exception as e:
self._logger.warning(f"删除本地截图失败: {e}")
else:
# HTTP 上报失败,进入重试
self._handle_retry(alarm_json, "HTTP 上报失败")
@@ -277,13 +258,13 @@ class AlarmUploadWorker:
self._logger.warning(f"告警结束上报异常: {e}")
def _upload_snapshot_to_cos(
self, local_path: str, alarm_id: str, device_id: str
self, image_bytes: bytes, alarm_id: str, device_id: str
) -> Optional[str]:
"""
上传截图到腾讯云 COS
上传截图到腾讯云 COS(直接从内存字节流上传)
Args:
local_path: 本地截图路径
image_bytes: JPEG 图片字节
alarm_id: 告警ID
device_id: 设备ID
@@ -292,8 +273,8 @@ class AlarmUploadWorker:
"""
cos_cfg = self._settings.cos
if not cos_cfg.secret_id or not cos_cfg.bucket:
self._logger.warning("COS 未配置,跳过截图上传")
return ""
self._logger.error("COS 未配置(缺少 secret_id 或 bucket无法上传截图")
return None
# 懒初始化 COS 客户端
if self._cos_client is None:
@@ -317,10 +298,11 @@ class AlarmUploadWorker:
object_key = f"alarms/{device_id}/{date_str}/{alarm_id}.jpg"
try:
self._cos_client.put_object_from_local_file(
self._cos_client.put_object(
Bucket=cos_cfg.bucket,
LocalFilePath=local_path,
Body=image_bytes,
Key=object_key,
ContentType="image/jpeg",
)
self._stats["cos_uploaded"] += 1
self._logger.info(f"COS 上传成功: {object_key}")