import requests import os import sys import uuid import hmac import time import hashlib import base64 from collections import OrderedDict from dotenv import load_dotenv # 加载环境变量(AK/SK) load_dotenv() # ========== 核心配置(替换成你的信息) ========== LIBLIBAI_AK = os.getenv("LIBLIBAI_ACCESS_KEY") # 从.env读取或直接写死 LIBLIBAI_SK = os.getenv("LIBLIBAI_SECRET_KEY") LOCAL_IMAGE_PATH = r"C:\Users\11304\gitlab\cybertogether\tool_agent\control_reference.png" # 你的本地图片路径 LIBLIBAI_DOMAIN = "https://openapi.liblibai.cloud" # ========== 核心工具函数 ========== def generate_auth_url(uri, ak, sk): """生成带签名的请求URL(仅用于获取OSS签名)""" ts = str(int(time.time() * 1000)) nonce = uuid.uuid4().hex sign_str = f"{uri}&{ts}&{nonce}" # 生成签名 dig = hmac.new(sk.encode(), sign_str.encode(), hashlib.sha1).digest() sig = base64.urlsafe_b64encode(dig).rstrip(b"=").decode() # 拼接URL return f"{LIBLIBAI_DOMAIN}{uri}?AccessKey={ak}&Timestamp={ts}&SignatureNonce={nonce}&Signature={sig}" def get_oss_upload_signature(file_name, ak, sk): """获取OSS上传签名""" # 1. 提取文件扩展名 ext = os.path.splitext(file_name)[1].lstrip('.').lower() if ext not in ['jpg', 'jpeg', 'png']: print(f"❌ 不支持的文件格式: {ext},仅支持jpg/png/jpeg") return None # 2. 调用签名接口 uri = "/api/generate/upload/signature" auth_url = generate_auth_url(uri, ak, sk) headers = {'Content-Type': 'application/json'} payload = { "name": file_name, # 完整文件名(含扩展名) "extension": ext } try: print(f"📡 正在请求OSS签名... URL: {auth_url[:100]}...") resp = requests.post(auth_url, headers=headers, json=payload, timeout=10) resp.raise_for_status() result = resp.json() if result.get("code") != 0: print(f"❌ 获取签名失败: {result.get('msg')}") return None sig_data = result.get("data") print(f"✅ 获取签名成功!") print(f" - postUrl: {sig_data['postUrl']}") print(f" - key: {sig_data['key']}") return sig_data except Exception as e: print(f"❌ 请求签名接口异常: {str(e)}") return None def upload_to_oss(sig_data, local_file_path): """上传本地文件到OSS""" # 1. 校验文件 if not os.path.exists(local_file_path): print(f"❌ 本地文件不存在: {local_file_path}") return None file_name = os.path.basename(local_file_path) ext = os.path.splitext(local_file_path)[1].lower() content_type = 'image/jpeg' if ext in ['.jpg', '.jpeg'] else 'image/png' # 2. 构造表单参数(必须用OrderedDict保证顺序,file放最后) form_data = OrderedDict([ ('key', sig_data['key']), ('policy', sig_data['policy']), ('x-oss-date', sig_data['xOssDate']), ('x-oss-expires', str(sig_data['xOssExpires'])), ('x-oss-signature', sig_data['xOssSignature']), ('x-oss-credential', sig_data['xOssCredential']), ('x-oss-signature-version', sig_data['xOssSignatureVersion']) ]) # 3. 读取文件并上传 try: print(f"📤 正在上传文件到OSS...") print(f" - 本地文件: {local_file_path}") print(f" - OSS地址: {sig_data['postUrl']}") with open(local_file_path, 'rb') as f: files = { 'file': (file_name, f, content_type) # file必须是最后一个字段 } # 发送上传请求 resp = requests.post( sig_data['postUrl'], data=form_data, files=files, timeout=30, headers={'User-Agent': 'Mozilla/5.0'} ) # 4. 输出上传结果 print(f"\n📋 上传响应信息:") print(f" - 状态码: {resp.status_code}") print(f" - 响应头: {dict(resp.headers)}") print(f" - 响应体: {resp.text[:500] if resp.text else '空'}") if resp.status_code in [200, 204]: # 拼接最终的OSS URL(仅用于LiblibAI API调用,浏览器访问会403) oss_url = f"{sig_data['postUrl']}/{sig_data['key']}" print(f"\n✅ 上传成功!OSS URL: {oss_url}") print(f"⚠️ 注意:该URL仅能用于LiblibAI API调用,浏览器直接访问会返回403(私有读权限)") return oss_url else: print(f"\n❌ 上传失败!状态码: {resp.status_code}") return None except Exception as e: print(f"\n❌ 上传过程异常: {str(e)}") return None # ========== 主执行逻辑 ========== if __name__ == "__main__": # 1. 基础校验 if not LIBLIBAI_AK or not LIBLIBAI_SK: print("❌ 请先配置LIBLIBAI_ACCESS_KEY和LIBLIBAI_SECRET_KEY(.env文件或直接写死)") sys.exit(1) if not os.path.exists(LOCAL_IMAGE_PATH): print(f"❌ 本地图片不存在: {LOCAL_IMAGE_PATH}") sys.exit(1) # 2. 获取OSS签名 file_name = os.path.basename(LOCAL_IMAGE_PATH) sig_data = get_oss_upload_signature(file_name, LIBLIBAI_AK, LIBLIBAI_SK) if not sig_data: sys.exit(1) # 3. 上传图片到OSS upload_to_oss(sig_data, LOCAL_IMAGE_PATH)