收藏
回答

使用云托管的对象存储,python后端怎么获取前端可回显的地址?

def upload_file_stream(user_id: str, filename: str, data: bytes, content_type: str) -> Dict[str, str]:
    """
    统一的对象存储上传入口:
    - 环境变量 STORAGE_DRIVER=COS 时,使用腾讯云 COS
    - 否则走本地目录 STORAGE_LOCAL_DIR(默认 /tmp/saas_uploads)
    返回:
      - key: 对象键(路径)
      - url: 可访问的URL(COS为公网,LOCAL需自行映射静态目录或开发使用)
    """
    driver = (os.getenv("STORAGE_DRIVER") or "LOCAL").upper()
    ts = int(time.time())
    fid = uuid.uuid4().hex[:12]
    fname = _safe_filename(filename)
    key = f"uploads/{user_id}/{ts}-{fid}-{fname}"


    if driver == "COS":
        # 期望环境变量:
        # COS_BUCKET, COS_REGION
        # 可选:COS_SECRET_ID, COS_SECRET_KEY (若不填则尝试获取微信云托管临时密钥)
        # 可选:COS_BASE_URL (自定义CDN域名)
        bucket = os.getenv("COS_BUCKET")
        region = os.getenv("COS_REGION")
        base_url = os.getenv("COS_BASE_URL")
        
        secret_id = os.getenv("COS_SECRET_ID")
        secret_key = os.getenv("COS_SECRET_KEY")
        token = None


        # 如果没有配置永久密钥,尝试获取微信云托管临时密钥
        if not (secret_id and secret_key):
            try:
                # 微信云托管内部鉴权接口
                resp = urllib_request.urlopen("http://api.weixin.qq.com/_/cos/getauth", timeout=3)
                if resp.status == 200:
                    auth_data = json.loads(resp.read().decode('utf-8'))
                    secret_id = auth_data.get("TmpSecretId")
                    secret_key = auth_data.get("TmpSecretKey")
                    token = auth_data.get("Token")
            except Exception:
                # 忽略错误,后续检查会处理缺失情况
                pass
        
        # 调试输出
        if not all([secret_id, secret_key, bucket, region]):
             print(f"[DEBUG] Missing COS Config: bucket={bucket}, region={region}, has_secret_id={bool(secret_id)}, has_secret_key={bool(secret_key)}")
             
        if not all([secret_id, secret_key, bucket, region]):
            raise RuntimeError("COS config missing: COS_BUCKET|COS_REGION is required. COS_SECRET_ID|COS_SECRET_KEY is required unless in WXCloud environment.")


        try:
            # 仅在启用 COS 时尝试导入,避免未安装时报错
            from qcloud_cos import CosConfig, CosS3Client
        except Exception:
            raise RuntimeError("Missing dependency: cos-python-sdk-v5. Please `pip install cos-python-sdk-v5`")


        config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token)
        client = CosS3Client(config)
        client.put_object(
            Bucket=bucket,
            Body=data,
            Key=key,
            ContentType=content_type or "application/octet-stream",
        )
        if base_url:
            url = f"{base_url.rstrip('/')}/{key}"
        else:
            url = f"https://{bucket}.cos.{region}.myqcloud.com/{key}"
        
        # 构造 file_id (云托管环境)
        # 格式通常为: cloud://<ENV_ID>.<BUCKET>-<APPID>/<KEY>
        # 但 bucket 名字通常已经是 <name>-<appid>
        # 如果能获取到 WX_CLOUD_ENV_ID,则尝试构造
        env_id = os.getenv("WX_CLOUD_ENV_ID")
        file_id = None
        if env_id:
            # 假设 COS_BUCKET 已经是完整名 (name-appid)
            # 云托管的 fileID 格式有点 trick,这里尝试最通用的 cloud://<ENV_ID>.<BUCKET>/<KEY>
            # 注意:如果 bucket 不是该环境默认的 bucket,这个 file_id 可能无效
            file_id = f"cloud://{env_id}.{bucket}/{key}"
        
        # 生成一个短期有效的签名 URL,确保即使 Bucket 是私有的,前端上传后也能立即回显
        signed_url = client.get_presigned_url(
            Method='GET',
            Bucket=bucket,
            Key=key,
            Expired=3600
        )
        # 微信云托管基于 Gunicorn/Flask,标准输出可能被缓存或日志级别过滤
        # 使用 flush=True 强制刷新缓冲区,确保日志即时输出到云托管控制台
        print('signed_url: ', signed_url, flush=True)
        return {"key": key, "url": url, "file_id": file_id, "signed_url": signed_url}


返回的signed_url,直接用浏览器打开报错


回答关注问题邀请回答
收藏

1 个回答

  • 智能回答 智能回答 本次回答由AI生成
    01-26
    有用
登录 后发表内容
问题标签