> ## Documentation Index
> Fetch the complete documentation index at: https://docs.geeknow.top/llms.txt
> Use this file to discover all available pages before exploring further.

# 上传图片

> 调用 `/api/upload/presign` 生成预签名上传地址，再把返回的 `public_url` 传给接受公网素材 URL 的接口。

# 上传图片

这个接口不直接接收文件内容，而是先签发一个对象存储 `PUT` 地址。客户端拿到地址后再自行上传文件。

* 适合图片、视频首帧和其他媒体素材的预上传。
* 使用 API Key 认证，但返回结构是 `/api/*` 的 `success/message/data` 包装。
* 默认过期时间为 `900` 秒，最短 `60` 秒，最长 `3600` 秒。
* 未显式传 `prefix` 时，会按当前用户自动生成 `uploads/user/{id}` 前缀。

## 方法与路径

```http theme={null}
POST /api/upload/presign
```

## 请求示例

<CodeGroup>
  ```bash theme={null}
  curl -X POST https://www.geeknow.top/api/upload/presign \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "file_name": "input.png",
      "content_type": "image/png",
      "expires_in": 900
    }'
  ```

  ```python theme={null}
  import requests

  resp = requests.post(
      "https://www.geeknow.top/api/upload/presign",
      headers={
          "Authorization": "Bearer YOUR_API_KEY",
          "Content-Type": "application/json",
      },
      json={
          "file_name": "input.png",
          "content_type": "image/png",
          "expires_in": 900,
      },
      timeout=30,
  )
  print(resp.json())
  ```

  ```javascript theme={null}
  const response = await fetch("https://www.geeknow.top/api/upload/presign", {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      file_name: "input.png",
      content_type: "image/png",
      expires_in: 900,
    }),
  });

  console.log(await response.json());
  ```
</CodeGroup>

## 响应示例

<ResponseExample>
  ```json 200 - 预签名地址已创建 theme={null}
  {
    "success": true,
    "message": "",
    "data": {
      "method": "PUT",
      "upload_url": "https://.../uploads/user/12/20260519-abcdef.png?signature=...",
      "object_key": "uploads/user/12/20260519-abcdef.png",
      "public_url": "https://.../uploads/user/12/20260519-abcdef.png",
      "expires_in": 900,
      "content_type": "image/png"
    }
  }
  ```

  ```json 200 - OSS 未配置 theme={null}
  {
    "success": false,
    "message": "oss storage is not configured"
  }
  ```

  ```json 401 theme={null}
  {
    "success": false,
    "message": "无效的令牌"
  }
  ```
</ResponseExample>

## 认证

```http theme={null}
Authorization: Bearer YOUR_API_KEY
```

## Body

<ParamField body="file_name" type="string">
  原始文件名。服务端会根据扩展名生成对象键。未传时也可以正常工作，但通常建议显式传入。
</ParamField>

<ParamField body="content_type" type="string">
  文件 MIME 类型，例如 `image/png`、`image/jpeg`、`video/mp4`。
</ParamField>

<ParamField body="expires_in" type="integer">
  预签名 URL 有效期，单位秒。服务端会自动限制在 `60` 到 `3600` 之间。
</ParamField>

<ParamField body="prefix" type="string">
  对象键前缀。未传时默认使用 `uploads/user/{id}`。
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  是否成功。`/api/*` 接口优先用这个字段表示业务结果。
</ResponseField>

<ResponseField name="data.method" type="string">
  上传方法，当前固定为 `PUT`。
</ResponseField>

<ResponseField name="data.upload_url" type="string">
  带签名的对象存储上传地址。
</ResponseField>

<ResponseField name="data.public_url" type="string">
  上传成功后可直接传给图像、视频和任务接口的公网 URL。
</ResponseField>

## 使用场景

### 上传后再用于素材 URL 接口

1. 调用 `/api/upload/presign` 取得 `upload_url` 与 `public_url`。
2. 对 `upload_url` 发起 `PUT` 上传文件。
3. 在接受公网素材 URL 的接口中把 `public_url` 作为输入字段传入。

## 注意事项

<Warning>
  这个接口只负责签发上传地址，不会替你上传文件本身。真正的文件上传需要客户端对 `upload_url` 再发一次 `PUT` 请求。
</Warning>

## 相关接口

* [视频系列概览](../videos/overview)
* [图像系列概览](../images/overview)
