> This page is for Camera to Cloud.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# 操作指南：上传（高级）

## 前言





可靠的资产上传是每个 C2C 集成的核心功能。 本指南提供了用于创建稳健、具有韧性且高效上传系统的高级技术和最佳做法，即使在充满挑战性的环境中也能表现出色。





## 前提条件

如果您还没有阅读，请在继续之前先查阅[实施 C2C：设置](./implementing-c2c-setting-up)指南。 您需要用到在[身份验证和授权流程](./implementing-c2c-authentication-and-authorization)中获取的 `access_token`。 我们将继续使用[基础上传指南](./how-to-basic-upload)中的[同一个测试资产](https://f.io/Rq1q5CzB)。

## 高级资产参数

在 Frame.io 中创建资产时，您可以使用多个高级参数来自定义上传行为。 其中，`offset` 参数对于正确集成尤为重要。

### Offset - 处理暂停的设备

提供准确的 `offset` 值至关重要。 此参数用于指定媒体文件的创建时间，并确保您的设备不会上传本不该分享的内容。 当设备在 Frame.io 中暂停时，表示用户希望暂停期间创建的媒体文件不被上传。 有关更多详情，请参阅我们[关于暂停功能的指南](./how-to-heartbeats-connection-info-and-status#handling-the-paused-status)。

### offset 参数的其他优势

`offset` 参数为在 Frame.io 中组织媒体文件提供了另一个显著优势。 当上传在较早日期拍摄的内容时（例如，用户在回放期间选择上周拍摄的一张照片），`offset` 参数能够确保该媒体文件出现在与其原始拍摄日期对应的文件夹中，而不是当前上传日期对应的文件夹中。 这种按时间顺序组织的机制，能够在 Frame.io 的项目结构中保持逻辑清晰的时间线。 如果没有 `offset` 参数，历史媒体文件会被错误地与今天的内容归类在一起，这可能会给剪辑师及其他协作者造成困惑。 您可以通过自己的界面为用户提供此方面的选择。 如果用户希望无论媒体文件何时拍摄，都按当前日期对所有上传内容进行归类，您只需省略 `offset` 参数即可，因为当未指定该参数时，其默认值为 `0`。

我们的 API 设计使得您的设备无需跟踪暂停状态。 在上传文件时，您只需指明该文件是在多少秒之前创建的。 我们的服务器会将该信息与暂停窗口期进行比较，如果文件是在暂停期间创建的，则会拒绝上传。





为了演示此功能，请通过“C2C Connections”选项卡中的三点菜单暂停您的设备。





现在尝试上传一个资产：





```shell
{
curl -X POST https://api.frame.io/v2/devices/assets \
    --header 'Authorization: Bearer [access_token]' \
    --header 'Content-Type: application/json' \
    --header 'x-client-version: 2.0.0' \
    --data-binary @- <<'__JSON__' 
        {
            "name": "C2C_TEST_CLIP.mp4", 
            "filetype": "video/mp4", 
            "filesize": 21136250,
            "offset": 0
        }
__JSON__
} | python -m json.tool
```




<Info title="API 端点规范">
  `/v2/devices/assets` 的文档可以在[此处找到](/camera-to-cloud/api-reference/device-asset-create)。
</Info>


您将收到以下错误：





```json
{
    "code": 409,
    "errors": [
        {
            "code": 409,
            "detail": "The channel you're uploading from is currently paused.",
            "status": 409,
            "title": "Channel Paused"
        }
    ],
    "message": "Channel Paused"
}
```





如果您取消暂停设备并使用相同的请求重试，则会创建资产。

但是，如果该资产是在暂停窗口期内创建的，您需要设置 `offset` 参数以反映其实际创建时间：

```shell
{
curl -X POST https://api.frame.io/v2/devices/assets \
    --header 'Authorization: Bearer [access_token]' \
    --header 'Content-Type: application/json' \
    --header 'x-client-version: 2.0.0' \
    --data-binary @- <<'__JSON__' 
        {
            "name": "C2C_TEST_CLIP.mp4", 
            "filetype": "video/mp4", 
            "filesize": 21136250,
            "offset": 60
        }
__JSON__
} | python -m json.tool
```

这告诉 Frame.io 该资产是在 60 秒前（暂停期间）创建的，从而正确触发`通道已暂停`错误。 *准确的 `offset` 值至关重要*，可防止违背用户意愿上传敏感内容，包括受保护的知识产权、敏感素材或其他受限制的材料。
<Error title="偏移和重试">
  当重试一次失败的资产创建调用时，请记得更新 `offset` 值。 在较长的重试周期中，一个固定不变的 offset 值可能会偏离相关的暂停窗口期，从而可能导致本应被阻止的上传请求得以通过。
</Error>


### 上传到指定通道





如果您的设备有多个通道，您可以指定要使用哪个通道：





```shell
{
curl -X POST https://api.frame.io/v2/devices/assets \
    --header 'Authorization: Bearer [access_token]' \
    --header 'Content-Type: application/json' \
    --header 'x-client-version: 2.0.0' \
    --data-binary @- <<'__JSON__' 
        {
            "name": "C2C_TEST_CLIP.mp4", 
            "filetype": "video/mp4", 
            "filesize": 21136250,
            "offset": -10,
            "channel": 2
        }
__JSON__
} | python -m json.tool
```

如果未指定，默认通道为 `0`。 大多数集成无需更改此值。

### 请求自定义分片数量

默认情况下，Frame.io 的后端会将文件划分为大约 25 MB 大小的分片。 对于高拥堵的网络环境，您可能希望使用更小的分片。 您可以使用 `parts` 参数请求指定数量的分片：

```shell
{
curl -X POST https://api.frame.io/v2/devices/assets \
    --header 'Authorization: Bearer [access_token]' \
    --header 'Content-Type: application/json' \
    --header 'x-client-version: 2.0.0' \
    --data-binary @- <<'__JSON__'
        {
            "name": "C2C_TEST_CLIP.mp4",
            "filetype": "video/mp4",
            "filesize": 21136250,
            "offset": 0,
            "parts": 4
        }
__JSON__
} | python -m json.tool
```





该响应将包含四个上传 URL：





```json
{
    ...
    "upload_urls": [
        "https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-01-path]",
        "https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-02-path]",
        "https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-03-path]",
        "https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-04-path]"
    ],
    ...
}
```





分片大小为：





**`Python`**

```python title="Python"
math.ceiling(float(21136250) / float(4))
# 5284063 bytes
```

最后一个分片将为 5,284,061 字节（计算方式：`21136250 - 5284063 * 3`）。 请求自定义分片数量时，请注意 [AWS S3 分段上传限制](https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html)：
* 每个分段大小必须至少为 5 MiB（5,242,880 字节），最后一个分段除外
* 总分段数不得超过 10,000 个

如果您的请求违反了这些限制，您将收到 `500：内部服务器错误`：

```
{
    "code": 500,
    "errors": [
        {
            "code": 500,
            "detail": "There was a problem with your request",
            "status": 500,
            "title": "Something went wrong"
        }
    ],
    "message": "Something went wrong"
}
```





请务必确保您的自定义分段数量符合 S3 的要求。





## 高效上传





C2C 设备通常在充满挑战性的网络环境中运行，因此效率至关重要。 以下是一些最大化吞吐量的策略。





### TCP 连接重用/连接池

建立加密连接需要大量的协商开销。 为了高效运行，在发出多次请求时可重复使用 TCP 连接。 大多数 HTTP 库都提供了`客户端`或`会话`抽象，用于维护持久连接。

一个新的 HTTPS 连接的协商过程包括加密握手和证书验证。 通过重用连接，您只需承担一次此开销，而不是对每个请求都执行一次。




<Info title="TCP 握手参考">
  有关 TLS 握手过程的技术详情，请参阅 [Cloudflare 的说明](https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/)。
</Info>
 为了演示使用 `curl` 进行连接重用，首先按照[基础上传指南](/camera-to-cloud/how-to-basic-upload#step-1-creating-an-asset)中的描述在 Frame.io 中创建一个新资产。

接下来，将文件拆分为单独的分片进行测试：





```shell
head -c 10568125 ~/Downloads/C2C_TEST_CLIP.mp4 > "C2C_TEST_CLIP-Chunk01"
tail -c 10568125 ~/Downloads/C2C_TEST_CLIP.mp4 > "C2C_TEST_CLIP-Chunk02"
```

现在使用 curl 的 `--next` 参数，通过单个 TCP 连接上传两个分片：

```shell
curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-1-path] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk01 \
--next -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-2-path] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk02
```





将其与单独连接的方式进行比较：





```shell
curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-1-path] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk01 \
&& curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-2-path] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk02
```




<Info title="重用分片 URL">
  


您可以多次上传到同一个分片 URL，因此能在不同示例之间随意重用这些 URL。



</Info>


在测试中，连接重用通常可将顺序上传的性能提高 15-20%。





### 并行上传





为了获得更高的吞吐量，可以同时上传多个分片：





```shell
curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-1-path] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk01 \
& \
curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[part-2-path]\
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk02 \
&
```





在带宽充足的情况下，并行上传的完成时间大约等于最慢的单个上传所需的时间。





为了实现最佳并行上传，一个实用的经验法则是：每个 CPU 核心对应 2 个并发上传。 超过这个比例可能会导致资源争用和收益递减。




<Warning title="并行上传速度">
  


网络状况会显著影响并行上传的性能。 在某些环境中，顺序上传可能比并行上传表现更好。 高级实施可以监测吞吐量并动态调整并发上传数量。 请务必在实际生产环境中进行性能分析，而不是依赖示例时间。



</Warning>


### 结合两种方法





为了达到最高效率，可以将连接池与并行上传相结合。 创建多个进程，每个进程在其自己的上传序列中使用连接池：





```shell
curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[asset01-chunk01] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk01 \
--next -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[asset01-chunk02] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk02 \
& \
curl -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[asset02-chunk01] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk01 \
--next -X PUT https://frameio-uploads-production.s3-accelerate.amazonaws.com/parts/[asset02-chunk02] \
        --include \
        --header 'content-type: video/mp4' \
        --header 'x-amz-acl: private' \
        --data-binary @C2C_TEST_CLIP-Chunk02 \
&
```




<Info title="HTTP 库功能">
  


大多数 HTTP 库都提供了用于连接池和并行请求的抽象。 请尝试您所使用库的选项，以确定适合您环境的最佳配置。



</Info>


## 跟踪上传进度





您的集成必须为用户提供基本的进度指示。 可以接受分片级别的粒度——对于一个包含三个分片的上传任务，随着每个分片完成，进度可能依次递增为：0% → 33% → 66% → 100%。





更细粒度的进度报告取决于您所使用的 HTTP 库的能力。 如果您需要关于实施更详细的进度跟踪的指导，请联系我们的团队。





## 可靠上传

为了实现稳健的错误处理，请查阅我们的[错误指南](/camera-to-cloud/how-to-handle-errors)。 以下各节假设您已实施了该指南中描述的错误处理策略。

创建一个可用于生产环境的上传程序，除了处理单个请求的错误之外，还需要考虑更多因素。





### 创建上传队列





在现实场景中，您的设备生成媒体文件的速度可能会超过其上传速度，或者可能遇到长时间的网络连接中断。 实施队列系统可以将媒体创建与上传管理分离。





考虑双队列架构：




1. 一个**媒体队列**，用于在 Frame.io 中注册本地文件
2. 一个**分片队列**，用于上传各个文件块





下面是一个简化的实施示例：





**`Python`**

```python title="Python"
# Where we are going to queue new files.
FILE_QUEUE = Queue()

# Where we are going to queue new chunks.
CHUNK_QUEUE = Queue()

# The http session that will handle TCP 
# connection pooling for us.
HTTP_SESSION = http.Session()

def take_picture():
    """Snaps a picture for the user."""

    image = MY_DEVICE.capture()
    file_path = MY_DEVICE.write_image(image)
    FILE_QUEUE.add(file_path)

def task_register_assets():
    """
    Pulls snapped pictures from the FILE_QUEUE, registers
    with Frame.io, and adds the chunks to the CHUNK_QUEUE.
    """
    while True:
        # Get the latest file added to the queue and register 
        # a C2C Asset for it.
        new_file = FILE_QUEUE.get()
        asset = c2c.crete_asset_for_file(HTTP_SESSION, new_file)

        # Calculate the size for each chunk
        chunk_size = c2c.calculate_chunk_size(asset, new_file)

        # Create a message for each chunk with it's parameters 
        # and add it to the 
        # queue.
        chunk_start = 0
        for chunk_url in asset.upload_urls:
            message = {
                "file_path": new_file,
                "chunk_url": chunk_url,
                "chunk_start": chunk_start,
                "chunk_size": chunk_size,
            }

            # Put the message in the queue and 
            CHUNK_QUEUE.put(message)
            chunk_start += chunk_size

def task_upload_chunk():
    """Takes a chunks and uploads them."""

    while True:
        info = CHUNK_QUEUE.get()
        c2c.upload_chunk(HTTP_SESSION, info)

def launch_upload_tasks():
    """Lauches our Frame.io upload tasks."""
    # Create a list to hold all of our tasks.
    tasks = list()

    # Create one task for registering assets.
    asset_task = run_task_in_thread(task_register_assets)
    tasks.append(asset_task)

    # Create 2 tasks per CPU core for uploading chunks.
    for _ in range(0, GET_CPU_COUNT() * 2):
        chunk_task = run_task_in_thread(task_upload_chunk)
        tasks.append(chunk_task)

    # Run these tasks until shutdown
    run_forever(tasks)
```




<Warning title="错误处理">
  在上述示例中，我们假设为 C2C 调用而调用的函数正按照[错误指南](/camera-to-cloud/how-to-handle-errors)中讨论的方式处理错误。
</Warning>


### 跨电源周期的持久队列





内存中的队列方法在设备保持通电状态下运行良好，但如果在上传完成前断电会怎样？ 为了创建一个真正具有韧性的集成，我们需要确保设备在重启后能够从中断处恢复。

这需要在电源周期之间将队列状态持久保存到存储中。 像 [SQLite](https://www.sqlite.org/index.html) 这样的嵌入式数据库为此功能提供了绝佳的基础。

您的持久队列实施应支持以下关键操作：




* 将新创建的文件添加到上传队列
* 跟踪资产在 Frame.io 中成功创建的时间
* 记录资产因错误而创建失败的情况
* 存储上传任务的文件分片信息
* 检索下一个要上传的分片
* 将分片标记为已成功上传
* 记录分片上传失败
* 提供文件状态信息以向用户展示




以下是我们如何调整之前的示例以使用持久存储系统：





**`Python`**

```python title="Python"
# Our persistence layer for queuing uploads, potentially using SQLite 
# or another embedded database
C2C_UPLOAD_STORE = NewC2CUploadStore()

# HTTP session for connection pooling
HTTP_SESSION = http.Session()

def take_picture():
    """Captures an image and adds it to the upload queue."""
    image = MY_DEVICE.capture()
    file_path = MY_DEVICE.write_image(image)

    # Register the file with our persistent store
    C2C_UPLOAD_STORE.add_file(file_path)

def task_register_assets():
    """
    Processes files from persistent storage and registers
    them with Frame.io for upload.
    """
    while True:
        # Get the next available file from our store
        file_record = C2C_UPLOAD_STORE.get_file()

        try:
            # Register the asset with Frame.io
            asset = c2c.create_asset_for_file(HTTP_SESSION, file_record)
            chunk_size = c2c.calculate_chunk_size(asset, file_record)

            # Create entries for each chunk in our persistent store
            chunk_start = 0
            for chunk_url in asset.upload_urls:
                message = {
                    "file_path": file_record,
                    "chunk_url": chunk_url,
                    "chunk_start": chunk_start,
                    "chunk_size": chunk_size,
                }
                
                C2C_UPLOAD_STORE.new_chunk(message)
                chunk_start += chunk_size
                
        except BaseException as error:
            # Record the error in our persistent store
            C2C_UPLOAD_STORE.file_asset_create_error(file_record, error)
        else:
            # Mark the asset as successfully created
            C2C_UPLOAD_STORE.file_asset_created(file_record)

def task_upload_chunk():
    """Uploads individual file chunks from the persistent queue."""
    while True:
        # Get the next chunk, marking it as "in progress" to prevent 
        # other tasks from processing it simultaneously
        chunk_record = C2C_UPLOAD_STORE.get_chunk()

        try:
            c2c.upload_chunk(HTTP_SESSION, chunk_record)
        except BaseException as error:
            # Record the error for potential retry
            C2C_UPLOAD_STORE.chunk_error(chunk_record, error)
        else:
            # Mark successful completion
            C2C_UPLOAD_STORE.chunk_success(chunk_record)

def launch_upload_tasks():
    """Launches Frame.io upload processing tasks."""
    tasks = []

    # Asset registration task
    asset_task = run_task_in_thread(task_register_assets)
    tasks.append(asset_task)

    # Multiple parallel chunk upload tasks
    worker_count = GET_CPU_COUNT() * 2
    for _ in range(worker_count):
        chunk_task = run_task_in_thread(task_upload_chunk)
        tasks.append(chunk_task)

    # Run indefinitely
    run_forever(tasks)
```





采用这种持久存储方法后，您的集成将能够抵御电源中断的影响。 当设备重新启动时，只需从其上次保存的状态继续处理。 此架构也为实施更高级的功能（如错误跟踪和检测上传停滞）奠定了基础。





### 跟踪上传错误

一个稳健的上传系统必须仔细跟踪错误。 使用[错误指南](/camera-to-cloud/how-to-handle-errors)中的策略重试某个操作后，请将这些失败记录到您的持久化存储中。 这样，您的系统就可以：
1. 降低有问题上传的优先级，防止它们阻塞整个队列
2. 向用户提供准确的状态信息
3. 针对持续存在的问题启用管理干预





当发生致命错误时，请标记该项，以避免不必要的重试尝试。





### 管理停滞的上传





实施防护措施，防止上传无限期停滞。 设置最长持续时间（例如 30 分钟），超过该时间后应终止并重新启动分片上传任务。 这样可以防止所有上传工作进程都被无响应操作阻塞的情况发生。





### 从静默故障中恢复





系统崩溃、断电或进程终止可能会阻止正常的错误报告。 从队列中检索项目时，请记录检出时间。 如果某个项目在超过合理阈值（例如 30 分钟）后仍处于“正在处理”状态，且未报告成功或失败，请自动将其返回到可用池中，以便由另一个工作进程处理。





### 缓解有毒上传





由于数据或环境本身的问题，“有毒”队列项目会持续失败。 如果这些项目不断重新排队，它们实际上可能会阻塞整个上传系统。 请考虑使用以下策略来处理此类情况：




* 经过多次失败后，降低项目的优先级，以便可以继续处理较新的内容
* 同时追踪显式错误和处理尝试次数
* 遵循连接和授权的最佳做法，以区分临时环境问题与文件本身的内在问题
* 实施递进式重试限制（例如，每个作业尝试内对单个操作重试 10 次，共尝试 3 个作业，总计 30 次尝试）
* 提供一个用户界面，以便在环境问题解决后手动重置有问题的上传




“有毒”上传可能由以下原因导致：



* 损坏的文件数据导致 I/O 错误
* 灾难性进程故障阻止错误报告
* 原本可重试的错误因永久性的潜在条件而被触发




### 系统重启后重试





在永久放弃有问题的上传之前，标记它们，以便在下次系统重启后进行最后一次重试。 这可以应对因内存、驱动程序或资源分配等临时系统状态问题而导致上传失败的情况。 如果某个上传在彻底重启后仍然失败，则可以更有把握地将其标记为永久性问题。





## 清空您的队列





请记得从队列中移除不可用的文件。 当媒体被物理移除或文件被删除时，请从上传队列中清除相应条目，以防止不必要的错误。





*重要的是，连接到新项目时必须清空上传队列。* 为一个项目排队的媒体绝不应出现在另一个项目中。 当用户将设备与不同项目进行配对时，请验证项目是否已更改；如果是，请完全清空现有队列。





## 后续步骤

我们鼓励您就任何疑问联系我们的团队，并继续参阅[高级上传指南](/camera-to-cloud/how-to-advanced-uploads)。 我们期待为您的集成进度提供支持。