Files
pod_trend_agent/graph/oss_upload.py
T
3218485270 5ab5cf6586 v110-v112 自定义模式完善 + 模板导出增强 + 多模态兼容优化
- 自定义模式:分析模型输出 delta 唯一改动指令,生图模板 custom_image_prompt.md({delta} 占位符),不再使用负向提示词;generate_design 按 custom_mode 分支,Pinterest 模式保留原创化指令,两模式互不影响
- 多模态分析 response_format 三级回退(json_schema → json_object → none),兼容 DeepSeek
- 模板导出:details 扩展列(细节1/2/3)、target_audience 扩展列(适用人群1)、固定值风格1=休闲/风格2=运动
- 童装特征库更新 + 标题模板外部化 + 图源映射增强
2026-09-03 18:28:39 +08:00

99 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""阿里云 OSS 图床:图片压缩(3:4、≥1340×1785、<2MB+ 上传。
- compress_for_oss(image_path, out_path)
中心裁剪到 3:4 → 缩放/放大到 1340×1785 → JPEG quality 迭代压到 <2MB。
- upload_to_oss(cfg, local_path, object_key)
用 oss2 上传到 config.oss 指定的 bucket,返回可访问 URL。
- build_oss_key(country, timestamp, code, rand4)
key = {国家}/{时间戳}/{货号}_{4位随机}.jpg(货号=前缀+3位计数,000 起最多 999)
- oss 配置(config.yaml oss 段):
oss_bucket / oss_endpoint / oss_key_id / oss_key_secret(或环境变量 OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET
"""
import io
from pathlib import Path
from typing import Optional
from PIL import Image
# 目标规格:3:4 宽高比,最小 1340×1785,文件 < 2MB
TARGET_W, TARGET_H = 1340, 1785
MAX_BYTES = 2 * 1024 * 1024
def compress_for_oss(image_path: str, out_path: str, max_bytes: int = MAX_BYTES) -> str:
"""压缩图片到 3:4 / ≥1340×1785 / <2MBJPEG)。返回输出路径。"""
with Image.open(image_path) as im:
im = im.convert("RGB")
# 1) 中心裁剪到 3:4
w, h = im.size
if w / h > 3 / 4: # 太宽 → 裁左右
new_w = int(h * 3 / 4)
x0 = (w - new_w) // 2
im = im.crop((x0, 0, x0 + new_w, h))
elif w / h < 3 / 4: # 太高 → 裁上下
new_h = int(w * 4 / 3)
y0 = (h - new_h) // 2
im = im.crop((0, y0, w, y0 + new_h))
# 2) 缩放到目标尺寸(≥1340×1785,正好 3:4
im = im.resize((TARGET_W, TARGET_H), Image.LANCZOS)
# 3) JPEG quality 迭代,保证 <2MB
out = Path(out_path)
out.parent.mkdir(parents=True, exist_ok=True)
for quality in (92, 85, 78, 70, 62, 55, 48, 40):
buf = io.BytesIO()
im.save(buf, "JPEG", quality=quality, optimize=True, progressive=True)
if buf.tell() <= max_bytes:
out.write_bytes(buf.getvalue())
return str(out)
# 全部超限 → 用最低质量兜底(仍可能 >2MB,打印警告)
out.write_bytes(buf.getvalue())
print(f"[oss] 警告: {Path(image_path).name} 压缩后仍 {buf.tell()/1024/1024:.1f}MB > 2MB(质量 {quality}")
return str(out)
def upload_to_oss(cfg: dict, local_path: str, object_key: str) -> Optional[str]:
"""上传本地文件到 OSS,返回 URL;配置缺失/失败返回 None(不中断)。"""
bucket = (cfg or {}).get("oss_bucket") or ""
endpoint = (cfg or {}).get("oss_endpoint") or ""
key_id = (cfg or {}).get("oss_key_id") or ""
key_secret = (cfg or {}).get("oss_key_secret") or ""
if not (bucket and endpoint and key_id and key_secret):
print("[oss] 配置缺失(config.oss),跳过上传")
return None
try:
import oss2
# 直连 session:忽略环境代理(挂 VPN 时代理会拦截国内 OSS);
# oss2.Bucket 的 session 必须是 oss2.Session(内部封装 requests),设其底层 trust_env=False
_session = oss2.Session()
try:
_session.session.trust_env = False
except AttributeError:
pass
auth = oss2.Auth(key_id, key_secret)
bkt = oss2.Bucket(auth, endpoint, bucket, session=_session)
with open(local_path, "rb") as f:
bkt.put_object(object_key, f)
url = f"https://{bucket}.{endpoint}/{object_key}"
print(f"[oss] 已上传: {url}")
return url
except Exception as e: # noqa: BLE001
print(f"[oss] 上传失败 {local_path}: {e}")
return None
def build_oss_key(country: str, timestamp: str, code: str, rand4: str,
ext: str = "", local: Optional[str] = None) -> str:
"""对象 key{国家}/{时间戳}/{货号}_{4位随机}.jpg(如 GB/20260820150945/DG000_Ab3x.jpg)。
ext 未显式指定时,优先从 local(实际待上传文件)的真实后缀推导——
文件可能是 png/jpg 而非固定 jpglocal 也未提供时回退 ".jpg"。
"""
if not ext.strip() and local:
ext = Path(local).suffix if Path(local).suffix else ".jpg"
ext = ext.strip() or ".jpg"
safe = lambda s: "".join(c for c in (s or "") if c.isalnum() or c in "-_").strip()
return f"{safe(country)}/{safe(timestamp)}/{safe(code)}_{safe(rand4)}{ext}"