适用场景:上传本地 ZIP 文件创建任务 → 轮询进度 → 下载正射影像
官方文档参考:https://docs.webodm.org/reference/
流程总览
Step 1: 获取 Token(身份认证)
↓
Step 2: 上传本地 ZIP 文件 → 创建任务 → 获取 task_id
↓
Step 3: 轮询任务执行进度(含百分比)
↓
Step 4: 任务完成后下载正射影像 .tif 文件
Step 1:获取 Token
接口信息
| 项目 | 内容 |
|---|
| 方法 | POST |
| 路径 | /api/token-auth/ |
| 认证 | 无需 |
| Content-Type | application/json 或 application/x-www-form-urlencoded |
请求体
{
"username": "your_username",
"password": "your_password"
}
响应体
{
"token": "[ID已隐藏].eyJ1c2VybmFtZSI6ImFkbWluIn0.xxxxxx"
}
关键字段说明
| 字段 | 类型 | 说明 |
|---|
token | string | JWT Token,后续所有请求需在 Header 中携带 |
注意事项
- Token 默认 6小时 过期
- 过期后接口返回
403,响应体为 {"detail": "Signature has expired."},需重新调用此接口获取新 Token
- 所有后续请求 Header 格式固定为:
Authorization: JWT <token>
cURL 示例
curl -X POST http://localhost:8000/api/token-auth/ \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "yourpassword"}'
Step 2:上传 ZIP 文件创建任务
接口信息
| 项目 | 内容 |
|---|
| 方法 | POST |
| 路径 | /api/projects/{project_id}/tasks/ |
| 认证 | Authorization: JWT <token> |
| Content-Type | multipart/form-data |
{project_id} 为你要创建任务的目标项目 ID(整数)
| 字段名 | 类型 | 必填 | 说明 |
|---|
images | file | ✅ 是 | 本地 ZIP 文件(服务端会自动解压),字段名必须为 images |
name | string | 否 | 任务名称,便于识别 |
options | JSON string | 否 | ODM 处理参数(见下方选项说明) |
processing_node | integer | 否 | 指定处理节点 ID,不填则自动分配 |
options 参数说明(JSON 字符串格式)
[
{"name": "orthophoto-resolution", "value": 5},
{"name": "dsm", "value": true},
{"name": "dtm", "value": false},
{"name": "fast-orthophoto", "value": false}
]
常用 options 参数:
| 参数名 | 类型 | 默认值 | 说明 |
|---|
orthophoto-resolution | float | 5 | 正射影像分辨率(厘米/像素),值越小精度越高 |
dsm | bool | false | 是否生成数字地表模型(DSM) |
dtm | bool | false | 是否生成数字地形模型(DTM) |
fast-orthophoto | bool | false | 是否启用快速正射(精度略低但速度更快) |
resize-to | integer | 2048 | 影像预处理缩放像素大小,-1 为不缩放 |
响应体(201 Created)
{
"id": "[ID已隐藏]",
"project": 1,
"name": "我的任务",
"status": null,
"progress": 0.0,
"processing_time": -1,
"images_count": 0,
"created_at": "2026-04-14T08:00:00Z",
"options": [
{"name": "orthophoto-resolution", "value": 5}
],
"available_assets": []
}
关键字段说明
| 字段 | 类型 | 说明 |
|---|
id | string (UUID) | 任务 ID,后续查询进度和下载成果必须使用此值 |
project | integer | 所属项目 ID |
status | integer/null | 初始为 null,处理开始后变为数字状态码 |
progress | float | 初始为 0.0,处理中为 0.0~1.0 的小数 |
cURL 示例
curl -X POST http://localhost:8000/api/projects/1/tasks/ \
-H "Authorization: JWT eyJ0eXAi..." \
-F "images=@/path/to/your/images.zip" \
-F "name=测试任务" \
-F 'options=[{"name":"orthophoto-resolution","value":5}]'
Step 3:查询任务执行进度
接口信息
| 项目 | 内容 |
|---|
| 方法 | GET |
| 路径 | /api/projects/{project_id}/tasks/{task_id}/ |
| 认证 | Authorization: JWT <token> |
{task_id} 为 Step 2 响应中获取的 id 字段(UUID 格式)
响应体示例
{
"id": "[ID已隐藏]",
"project": 1,
"name": "我的任务",
"status": 20,
"last_error": null,
"progress": 0.45,
"processing_time": 125000,
"images_count": 120,
"created_at": "2026-04-14T08:00:00Z",
"available_assets": [],
"options": [
{"name": "orthophoto-resolution", "value": 5}
]
}
关键字段说明
| 字段 | 类型 | 说明 |
|---|
status | integer / null | 任务状态码(见下方状态码表) |
progress | float | 任务进度,范围 0.0 ~ 1.0,乘以 100 即为百分比 |
last_error | string / null | 失败时的错误信息 |
processing_time | integer | 已处理时长(毫秒),-1 表示尚未开始 |
available_assets | array | 任务完成后可用的成果文件名列表 |
任务状态码(status 字段)
| 数值 | 状态名 | 含义 |
|---|
null | NEW | 任务已创建,尚未排队 |
10 | QUEUED | 已加入队列,等待处理节点 |
20 | RUNNING | 正在处理中,可通过 progress 获取百分比进度 |
30 | FAILED | 处理失败,查看 last_error 获取原因 |
40 | COMPLETED | 处理完成,可下载成果 |
50 | CANCELED | 已取消 |
进度百分比计算
任务百分比进度 = progress × 100
例:progress = 0.45 → 进度为 45%
轮询建议
- 建议每 10~30 秒 轮询一次(影像处理耗时较长)
- 当
status == 40(COMPLETED)时停止轮询并进入 Step 4
- 当
status == 30(FAILED)时读取 last_error 字段排查原因
cURL 示例
curl http://localhost:8000/api/projects/1/tasks/[ID已隐藏]/ \
-H "Authorization: JWT eyJ0eXAi..."
Step 4:下载正射影像 TIF 文件
接口信息
| 项目 | 内容 |
|---|
| 方法 | GET |
| 路径 | /api/projects/{project_id}/tasks/{task_id}/download/orthophoto.tif |
| 认证 | Authorization: JWT <token> |
| 响应类型 | 二进制文件流(image/tiff) |
⚠️ 只有当 Step 3 中 status == 40(COMPLETED)且 available_assets 包含 orthophoto.tif 时,此接口才可用
确认可下载(通过 available_assets 判断)
Step 3 响应中,任务完成后 available_assets 字段示例:
{
"available_assets": [
"orthophoto.tif",
"dsm.tif",
"dtm.tif",
"point_cloud.las",
"textured_model.zip",
"all.zip"
]
}
确认列表中存在 "orthophoto.tif" 后再调用下载接口。
完整下载路径
GET /api/projects/{project_id}/tasks/{task_id}/download/orthophoto.tif
cURL 示例
curl -o orthophoto.tif \
-H "Authorization: JWT eyJ0eXAi..." \
"http://localhost:8000/api/projects/1/tasks/[ID已隐藏]/download/orthophoto.tif"
完整 Python 代码示例
import requests
import json
import time
# ========== 配置区 ==========
BASE_URL = "http://localhost:8000" # WebODM 地址
USERNAME = "admin"
PASSWORD = "yourpassword"
PROJECT_ID = 1 # 目标项目 ID
ZIP_PATH = "/path/to/images.zip" # 本地 ZIP 文件路径
OUTPUT_PATH = "orthophoto.tif" # 下载保存路径
POLL_INTERVAL = 15 # 轮询间隔(秒)
# ============================
# ---------- Step 1: 获取 Token ----------
print("=== Step 1: 获取 Token ===")
res = requests.post(
f"{BASE_URL}/api/token-auth/",
json={"username": USERNAME, "password": PASSWORD}
)
res.raise_for_status()
token = res.json()["token"]
headers = {"Authorization": f"JWT {token}"}
print(f"Token 获取成功")
# ---------- Step 2: 上传 ZIP 创建任务 ----------
print("\n=== Step 2: 上传 ZIP 文件,创建任务 ===")
options = json.dumps([
{"name": "orthophoto-resolution", "value": 5}
])
with open(ZIP_PATH, "rb") as f:
res = requests.post(
f"{BASE_URL}/api/projects/{PROJECT_ID}/tasks/",
headers=headers,
files={"images": ("images.zip", f, "application/zip")},
data={"name": "自动化任务", "options": options}
)
res.raise_for_status()
task = res.json()
task_id = task["id"]
print(f"任务创建成功,Task ID: {task_id}")
# ---------- Step 3: 轮询任务进度 ----------
print("\n=== Step 3: 轮询任务进度 ===")
STATUS_MAP = {
None: "NEW(已创建)",
10: "QUEUED(排队中)",
20: "RUNNING(处理中)",
30: "FAILED(失败)",
40: "COMPLETED(完成)",
50: "CANCELED(已取消)",
}
while True:
res = requests.get(
f"{BASE_URL}/api/projects/{PROJECT_ID}/tasks/{task_id}/",
headers=headers
)
res.raise_for_status()
info = res.json()
status = info.get("status")
progress = info.get("progress", 0.0) or 0.0
percent = round(progress * 100, 1)
status_text = STATUS_MAP.get(status, f"未知状态({status})")
print(f"状态: {status_text} | 进度: {percent}%")
if status == 40: # COMPLETED
print("✅ 任务处理完成!")
available = info.get("available_assets", [])
print(f"可用成果文件: {available}")
break
elif status == 30: # FAILED
error = info.get("last_error", "未知错误")
print(f"❌ 任务失败,错误信息: {error}")
exit(1)
elif status == 50: # CANCELED
print("⚠️ 任务已取消")
exit(1)
time.sleep(POLL_INTERVAL)
# ---------- Step 4: 下载正射影像 ----------
print("\n=== Step 4: 下载正射影像 TIF ===")
if "orthophoto.tif" not in available:
print("❌ 正射影像文件不可用,请检查任务配置")
exit(1)
res = requests.get(
f"{BASE_URL}/api/projects/{PROJECT_ID}/tasks/{task_id}/download/orthophoto.tif",
headers=headers,
stream=True
)
res.raise_for_status()
with open(OUTPUT_PATH, "wb") as f:
for chunk in res.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"✅ 正射影像下载完成,保存至: {OUTPUT_PATH}")
错误处理速查
| HTTP 状态码 | 原因 | 处理方式 |
|---|
400 | 请求参数错误(如 ZIP 字段名错误) | 检查 form-data 字段名是否为 images |
401 | 未携带 Token | 添加 Authorization: JWT <token> Header |
403 | Token 过期或无权限 | 重新执行 Step 1 获��新 Token |
404 | project_id / task_id 不存在 | 检查 ID 是否正确 |
500 | 服务器内部错误 | 检查 WebODM 服务日志 |
接口汇总速查表
| 步骤 | 方法 | 接口路径 | 说明 |
|---|
| Step 1 | POST | /api/token-auth/ | 获取 JWT Token |
| Step 2 | POST | /api/projects/{project_id}/tasks/ | 上传 ZIP 创建任务 |
| Step 3 | GET | /api/projects/{project_id}/tasks/{task_id}/ | 查询任务状态和进度 |
| Step 4 | GET | /api/projects/{project_id}/tasks/{task_id}/download/orthophoto.tif | 下载正射影像 |