Skip to content
On this page

Upload File

上传图片/视频资源,获得可用于创建项目的 URL

Overview

上传流程:

1. 调用 Prepare Upload Token 获得 ousToken + globalDomain
2. 判断文件大小:
   - 小于 blockSize: 单文件上传
   - 大于 blockSize: 分片上传
3. 轮询 Upload Status 直到 status=5(成功)
4. 获得 url 用于创建项目

Base URL

https://{globalDomain}/ous/api/v2/...
Header: ous-token-v2: {ousToken}

globalDomainousToken 来自 Prepare Upload Token


Single File Upload

适用于文件大小 < blockSize 的情况。

Endpoint

POST https://{globalDomain}/ous/api/v2/single/upload
Content-Type: multipart/form-data;charset=utf-8
Header: ous-token-v2: {ousToken}

Request

Form Data

ParameterRequiredTypeDescription
fileYesFile文件内容
md5YesString文件 MD5 值
metadataNoString元数据
customPrefixNoString自定义存储前缀
customFilenameNoString自定义文件名

Response

json
{
  "c": "0",
  "d": {
    "taskId": "3FO4K029K4A3"
  }
}

Code Example

python
import hashlib
import requests

def upload_single_file(file_path, upload_info):
    """单文件上传"""
    
    # 计算 MD5
    with open(file_path, "rb") as f:
        file_data = f.read()
        md5 = hashlib.md5(file_data).hexdigest()
    
    # 上传
    with open(file_path, "rb") as f:
        response = requests.post(
            f"https://{upload_info['global_domain']}/ous/api/v2/single/upload",
            headers={"ous-token-v2": upload_info["ous_token"]},
            files={"file": f},
            data={"md5": md5}
        )
    
    if response.json()["c"] != "0":
        raise Exception("上传失败")
    
    return response.json()["d"]["taskId"]

Block Upload (分片上传)

适用于文件大小 > blockSize 的情况。

Step 1: 初始化分片上传

POST https://{globalDomain}/ous/api/v2/block/upload/init
Header: ous-token-v2: {ousToken}

Request

ParameterRequiredTypeDescription
md5YesString文件 MD5 值
blocksYesLong总分片数
sizeYesLong文件总大小(字节)
nameYesString文件名
metadataNoString元数据
customPrefixNoString自定义存储前缀
customFilenameNoString自定义文件名

Response

json
{
  "c": "0",
  "d": {
    "taskId": "encrypted_task_id"
  }
}

Step 2: 上传分片

POST https://{globalDomain}/ous/api/v2/block/upload/part
Content-Type: multipart/form-data;charset=utf-8
Header: ous-token-v2: {ousToken}

Concurrency Limit

同一文件的分片并发数量不超过 2,超过返回错误码 6。

Request

ParameterRequiredTypeDescription
fileYesFile分片文件内容
blockYesInteger分片序号(从 1 开始)

Response

json
{
  "c": "0",
  "d": null
}

Code Example

python
def upload_large_file(file_path, upload_info):
    """分片上传大文件"""
    
    file_size = os.path.getsize(file_path)
    block_size = upload_info["block_size"]
    blocks = math.ceil(file_size / block_size)
    
    # 计算 MD5
    with open(file_path, "rb") as f:
        md5 = hashlib.md5(f.read()).hexdigest()
    
    # 初始化
    init_response = requests.post(
        f"https://{upload_info['global_domain']}/ous/api/v2/block/upload/init",
        headers={"ous-token-v2": upload_info["ous_token"]},
        data={
            "md5": md5,
            "blocks": blocks,
            "size": file_size,
            "name": os.path.basename(file_path)
        }
    )
    
    task_id = init_response.json()["d"]["taskId"]
    
    # 上传各分片(并发控制在2以内)
    with open(file_path, "rb") as f:
        for block_num in range(1, blocks + 1):
            chunk = f.read(block_size)
            
            response = requests.post(
                f"https://{upload_info['global_domain']}/ous/api/v2/block/upload/part",
                headers={"ous-token-v2": upload_info["ous_token"]},
                files={"file": chunk},
                data={"block": block_num}
            )
            
            print(f"分片 {block_num}/{blocks} 上传完成")
    
    return task_id

Abort Upload (中止上传)

POST https://{globalDomain}/ous/api/v2/block/upload/abort
Header: ous-token-v2: {ousToken}

无需参数,ousToken 已绑定 taskId。


Query Upload Status

轮询此接口直到上传完成。

GET https://{globalDomain}/ous/api/v2/upload/status
Header: ous-token-v2: {ousToken}

Response (Single File)

json
{
  "c": "0",
  "d": {
    "status": 5,
    "uploadKey": "path/2026/02/03/NGA5S6IKBIDN3RSQAAAAAAA8.jpg",
    "url": "https://cdn.kujiale.com/path/2026/02/03/NGA5S6IKBIDN3RSQAAAAAAA8.jpg",
    "md5": "c2ce57814593ae4e9fa8f1f473a3ed47",
    "obsTaskId": "3FO4K029K4A3"
  }
}

Response (Block Upload)

json
{
  "c": "0",
  "d": {
    "taskId": "3FO4K029K4A3",
    "status": 5,
    "uploadKey": "path/to/file.jpg",
    "url": "https://cdn.kujiale.com/path/to/file.jpg",
    "md5": "d41d8cd98f00b204e9800998ecf8427e",
    "blockUpload": {
      "id": "encrypted_block_task_id",
      "status": 5,
      "progressPercent": 100
    }
  }
}

Status Codes

StatusMeaningIs Final
0草稿
1基础检测完成
2内容检测完成
3排队上传
4上传中
5上传成功✓ 是
6上传失败✓ 是
7审核中
8手动中止✓ 是

Polling Strategy

python
def wait_for_upload(upload_info, timeout=30):
    """等待上传完成"""
    
    start_time = time.time()
    
    while time.time() - start_time < timeout:
        response = requests.get(
            f"https://{upload_info['global_domain']}/ous/api/v2/upload/status",
            headers={"ous-token-v2": upload_info["ous_token"]}
        )
        
        data = response.json()["d"]
        status = data["status"]
        
        if status == 5:  # 成功
            return data["url"]
        elif status in [6, 8]:  # 失败或中止
            raise Exception(f"上传失败: status={status}")
        
        progress = data.get("blockUpload", {}).get("progressPercent", 0)
        print(f"状态: {status}, 进度: {progress}%")
        
        time.sleep(0.5)  # 轮询间隔建议 200ms 以上
    
    raise Exception("上传超时")

# 使用示例
file_url = wait_for_upload(upload_info)
print(f"✓ 文件上传成功: {file_url}")

Error Codes

CodeMeaningDescription
1配置错误配置不存在/未开启
2文件大小异常文件大小异常
3文件格式异常文件格式异常
4文件内容异常文件内容异常
5内容安全检测异常图片/音频安全校验失败
6上传限制同时上传任务超限/并发过高
7文件上传中文件正在上传中
8分片操作失败文件初始化失败
9服务器上传限制服务器上传任务较多
10线程池满上传线程已满
11元数据超限文件元信息大小超过限制
12Token 异常Token 已被使用/未绑定任务
13任务异常上传任务不存在/已结束

Complete Upload Function

python
import os
import hashlib
import requests
import time
import math

def upload_file(file_path, upload_info):
    """
    完整上传流程:自动判断单文件/分片上传,并等待完成
    
    Args:
        file_path: 本地文件路径
        upload_info: 包含 ous_token, global_domain, block_size
    
    Returns:
        url: 上传成功后的文件 URL
    """
    
    file_size = os.path.getsize(file_path)
    block_size = upload_info["block_size"]
    
    # 计算 MD5
    with open(file_path, "rb") as f:
        md5 = hashlib.md5(f.read()).hexdigest()
    
    print(f"📤 上传文件: {os.path.basename(file_path)} ({file_size/1024/1024:.2f}MB)")
    
    # 判断上传方式
    if file_size < block_size:
        # 单文件上传
        print("   使用单文件上传")
        task_id = upload_single_file(file_path, upload_info)
    else:
        # 分片上传
        print(f"   使用分片上传 ({math.ceil(file_size/block_size)} 个分片)")
        task_id = upload_large_file(file_path, upload_info)
    
    # 等待上传完成
    url = wait_for_upload(upload_info)
    
    print(f"   ✓ 上传成功: {url}")
    return url

下一步: Create 3D Generation Project

基于 Marble API 文档结构