Skill: bagayalu/file-upload-test (version 1.0.1)

--- name: file-upload description: 上传文件到内部 BS3 存储（免签名）。Use when user asks to upload files, images, documents to storage, or get a shareable URL for a file. metadata: { "openclaw": { "emoji": "📤", "requires": { "bins": ["python3"], "pip": ["boto3", "botocore"] }, "install": [ { "id": "pip", "kind": "pip", "packages": ["boto3", "botocore"], "label": "Install boto3 and botocore via pip", }, ], }, } --- # File Upload Skill 上传文件到内部 BS3 存储，生成可分享的文件 URL。 --- ## 🔐 前置条件 ### 1. 安装依赖 ```bash pip3 install boto3 botocore ``` ### 2. 网络要求 - **内网访问**: 需要在内网环境使用 - **Endpoint**: `http://bs3-hb1.internal` - **免签名**: 无需配置 AWS credentials --- ## 📦 功能说明 ### 支持的操作 | 操作 | 说明 | 输入 | 输出 | |------|------|------|------| | `upload_file` | 上传本地文件 | 文件路径 | 文件 URL | | `upload_bytes` | 上传二进制数据 | 文件名 + 数据 | 文件 URL | ### 特性 - ✅ **自动去重**: 上传文件名自动添加 8 位 UUID 前缀，防止重名覆盖 - ✅ **免签名**: 内网免认证，直接上传 - ✅ **固定 Bucket**: `kkim-mario-claw` - ✅ **CDN 加速**: 返回 `bs3-hb1.corp.tencent.com` 域名 URL --- ## 🚀 使用方法 ### 命令行方式 ```bash # 上传文件 python3 ~/.openclaw/workspace/skills/file-upload/upload.py upload_file /path/to/file.png # 输出示例 https://bs3-hb1.corp.tencent.com/kim-mario-claw/abc12345_file.png ``` ### Python API 方式 ```python from upload import upload_file, upload_bytes # 上传文件 url = upload_file("my-image.png", "/path/to/file.png") print(url) # 上传二进制数据 with open("/path/to/file.png", "rb") as f: data = f.read() url = upload_bytes("my-image.png", data) print(url) ``` --- ## 📋 使用场景 ### 场景 1: 上传图片 **用户说:** - "帮我上传这张图片" - "把这个文件传到 BS3" - "生成一个图片的分享链接" **处理流程:** 1. 获取文件路径 2. 调用 `upload_file` 3. 返回 URL ### 场景 2: 上传文档 **用户说:** - "上传这个 PDF 文件" - "把报告传到存储" **处理流程:** 1. 获取文件路径 2. 调用 `upload_file` 3. 返回 URL ### 场景 3: 上传二进制数据 **用户说:** - "保存这段数据" - "上传这个截图" **处理流程:** 1. 获取二进制数据 2. 调用 `upload_bytes` 3. 返回 URL --- ## 📁 文件结构 ``` ~/.openclaw/workspace/skills/file-upload/ ├── SKILL.md # 技能文档 ├── upload.py # Python 脚本 └── .env # 可选配置（如需要） ``` --- ## ⚠️ 注意事项 ### 1. 内网限制 - 只能在内网使用 - Endpoint: `http://bs3-hb1.internal` - 外网无法访问 ### 2. Bucket 说明 - **Bucket**: `kkim-mario-claw` - **用途**: 临时存储，非永久保存 - **建议**: 重要文件请备份到其他存储 ### 3. 文件名处理 - 自动添加 8 位 UUID 前缀 - 示例：`file.png` → `a1b2c3d4_file.png` - 目的：防止同名文件覆盖 ### 4. 返回 URL 格式 ``` https://bs3-hb1.corp.tencent.com/kim-mario-claw/{uuid}_{filename} ``` ### 5. 有效期 - **链接有效期：7 天** - 超时后文件可能无法访问 - 重要文件请及时下载保存 ## 📞 常见问题 ### Q: 上传失败怎么办？ **检查项:** 1. ✅ 是否在内网 2. ✅ 是否安装了 `boto3` 和 `botocore` 3. ✅ 文件路径是否正确 4. ✅ 网络是否能访问 `bs3-hb1.internal` ### Q: 如何验证上传成功？ 访问返回的 URL，能正常下载/查看即成功。 ### Q: 文件会永久保存吗？ Bucket 名称包含 `temp`，建议作为临时存储，重要文件请备份。 --- *Last updated: 2026-03-07*

path: upload.py
content: #!/usr/bin/env python3
"""
File Upload Skill - 上传文件到快手内部 BS3 存储
"""

import sys
import os
from uuid import uuid4

import boto3
from botocore import UNSIGNED
from botocore.config import Config

# ============== 配置 ==============
BS3_ENDPOINT = os.getenv("BS3_ENDPOINT", "http://bs3-hb1.internal")
BS3_BUCKET = os.getenv("BS3_BUCKET", "kim-mario-claw")
BS3_REGION = os.getenv("BS3_REGION", "hb1")

# 初始化 S3 客户端（免签名）
# 使用 S3 直接初始化，避免 boto3 自动修改 endpoint
from botocore.session import Session

botocore_session = Session()
botocore_session.set_config_variable("region", BS3_REGION)

client = botocore_session.create_client(
    "s3",
    endpoint_url=BS3_ENDPOINT,
    config=Config(signature_version=UNSIGNED),
    use_ssl=False,
)


def _unique_key(key: str) -> str:
    """
    生成唯一文件键，添加 8 位 UUID 前缀防止重名覆盖
    
    Args:
        key: 原始文件名
        
    Returns:
        带 UUID 前缀的文件名
    """
    return f"{uuid4().hex[:8]}_{key}"


def upload_file(key: str, file_path: str) -> str:
    """
    上传本地文件到 BS3
    
    Args:
        key: 文件标识名（用于生成唯一键）
        file_path: 本地文件路径
        
    Returns:
        上传后的文件 URL（含有效期提示）
        
    Raises:
        FileNotFoundError: 文件不存在
        Exception: 上传失败
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件不存在：{file_path}")
    
    obj_key = _unique_key(key)
    
    with open(file_path, "rb") as data:
        client.put_object(
            Body=data,
            Bucket=BS3_BUCKET,
            Key=obj_key
        )
    
    url = f"https://bs3-hb1.corp.kuaishou.com/{BS3_BUCKET}/{obj_key}"
    return f"{url}\n\n⚠️ 注意：上传文件链接 7 天有效，请及时下载保存。"


def upload_bytes(key: str, data: bytes) -> str:
    """
    上传二进制数据到 BS3
    
    Args:
        key: 文件标识名（用于生成唯一键）
        data: 二进制数据
        
    Returns:
        上传后的文件 URL（含有效期提示）
    """
    obj_key = _unique_key(key)
    
    client.put_object(
        Body=data,
        Bucket=BS3_BUCKET,
        Key=obj_key
    )
    
    url = f"https://bs3-hb1.corp.kuaishou.com/{BS3_BUCKET}/{obj_key}"
    return f"{url}\n\n⚠️ 注意：上传文件链接 7 天有效，请及时下载保存。"


def main():
    """命令行入口"""
    if len(sys.argv) < 2:
        print("Usage: python3 upload.py <command> [args...]")
        print("")
        print("Commands:")
        print("  upload_file <key> <file_path>   - 上传文件")
        print("  upload_bytes <key> <file_path>  - 上传文件内容（二进制）")
        print("")
        print("Examples:")
        print("  python3 upload.py upload_file test.png /path/to/file.png")
        print("  python3 upload.py upload_bytes data.bin /path/to/data.bin")
        sys.exit(1)
    
    command = sys.argv[1]
    
    if command == "upload_file":
        if len(sys.argv) < 4:
            print("Error: upload_file requires <key> and <file_path>")
            sys.exit(1)
        key = sys.argv[2]
        file_path = sys.argv[3]
        try:
            url = upload_file(key, file_path)
            print(f"✅ Upload successful: {url}")
        except Exception as e:
            print(f"❌ Upload failed: {e}")
            sys.exit(1)
    
    elif command == "upload_bytes":
        if len(sys.argv) < 4:
            print("Error: upload_bytes requires <key> and <file_path>")
            sys.exit(1)
        key = sys.argv[2]
        file_path = sys.argv[3]
        try:
            with open(file_path, "rb") as f:
                data = f.read()
            url = upload_bytes(key, data)
            print(f"✅ Upload successful: {url}")
        except Exception as e:
            print(f"❌ Upload failed: {e}")
            sys.exit(1)
    
    else:
        print(f"Error: Unknown command '{command}'")
        sys.exit(1)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)