图片生成API异步任务文件下载Nbility

图片生成成功却下载失败:保存 Base64、续查任务与避免重复提交

区分图片生成、异步查询和文件下载,按 task_id 恢复任务,保存 url 或 b64_json,并在发布前校验图片,避免下载失败后重复发起生成。

查看接入文档
图片生成成功却下载失败:保存 Base64、续查任务与避免重复提交

生图接口已经返回成功,图片却没保存下来;或者程序等到超时,不知道那张图到底有没有生成。这时先保留响应和任务记录,确认停在哪一步,再决定下一次请求。

生成、查询任务、下载图片是三个不同动作。 已有结果就继续保存;已有 task_id 就查询原任务。提交结果不明时,先查记录,不要把同一条提示词再发一遍当作恢复。

本文依据 2026 年 11 月 9 日查阅的官方资料和本站图片接口说明,给出恢复顺序与一个离线验证过的 Base64 保存示例。这里的异步任务路径属于 Nbility;OpenAI 官方接口的返回规则会单独标明。

先确定:生成没完成,还是文件没存好

当前拿到什么说明什么接下来做什么
只有提交超时,没有响应不知道服务端是否已接受保留时间与请求标识,查服务记录
task_id,状态仍在等待或运行已创建任务,尚无最终结果GET 查询同一个任务
成功状态及图片 URL 或 Base64已取得图片结果下载或解码,校验后落盘
文件存在但无法打开文件可能不完整、格式不对或根本不是图片检查响应、文件类型和完整解码

图片请求按提交、任务查询、文件校验和保存分成不同阶段,下载失败从已有结果恢复

“显示已过期”也要看发生在哪一层。Cherry Studio #18460有一条中文原始报告:Windows、2.0.3,通过微信 clawbot 发送的图片在安卓端无法查看。它没有提供可用于核对的日志,后续只有闲置机器人回复。这个报告说明需要检查消息传递和接收端,不能据此认定模型没生成图片,或某个图片 URL 一定过期了。

如果本来就没有生成结果,例如参数不支持或安全检查拒绝,先按图片生成报错排查处理。本文关注已有任务或结果以后怎样继续。

URL、Base64 与异步响应不要混着解析

先看实际响应的结构,再选择保存方式:

路径需要读取的字段保存方式
同步图片结果,返回链接data[i].url下载对应文件
同步图片结果,返回 Base64data[i].b64_json严格解码,再检查文件
Nbility 异步提交data.task_id保存任务标识,继续查询
Nbility 异步任务完成data.status、data.image_urls 或 data.result_url从已有结果继续下载

这里的 i 是每个结果的下标;多张图片应逐项处理,不能只保存第一项后就认定整批完成。

OpenAI 官方 Images API 参考说明,GPT Image 模型返回 Base64,不支持旧的 response_format 参数。直接调用 OpenAI 时按官方模型接口传参。 本站图片 API另有结果归一化:可按其说明选择 url 或 b64_json,不能把网关提供的返回方式套到官方入口。

近期 Open WebUI #33874就记录了一个开发分支问题:模型字段留空时,实际选中 GPT Image,构造请求的条件却仍使用原配置,带入了不适用的参数。PR #33875已于 11 月 8 日合并;当天核对的 v0.13.3 标签中,两处判断已改用实际 model。这是请求构造的修正,不能用来证明所有图片下载故障都已修复。

有 task_id,就续查原任务

通过本站 X-New-Api-Async-Task: true 创建的任务,用原来的认证配置查询。下面是 GET 查询,不会重新发起生成;把占位值替换为实际任务 ID,Key 从现有私密配置注入环境变量:

umask 077
export IMAGE_TASK_ID="task_YOUR_EXISTING_TASK_ID"
curl --fail-with-body --silent --show-error   --dump-header task-headers.txt   --output task-result.json   -H "Authorization: Bearer $NBILITY_API_KEY"   "https://api.nbility.ai/v1/images/tasks/$IMAGE_TASK_ID"

读取 task-result.json 的 data.status:

  • submitted、queued、in_progress:仍需等待。当前本站实现对未结束任务返回 Retry-After: 3,按提示安排下一次查询,并给自己的等待循环设结束条件。
  • succeeded:读取图片结果,进入保存步骤。这个状态不代表你的本地磁盘或对象存储已经有文件。
  • failed:保存 fail_reason 并排查原因。再次发起生成是一个新操作,应明确决定。

状态文件、响应和结果链接保存在私密目录。记录至少包括任务 ID、提交时间、模型、请求参数摘要、当前状态,以及各张图片是否已保存。进程重启后先读这些记录,再恢复下一步。task_id 也不能无限期代替本地文件;本站任务与结果保留政策没有在本文核实,不承诺永久可查询或永久可下载。

恢复记录分别保存任务状态和文件保存状态,保留原任务标识并逐张记录本地结果

如果提交超时而没有拿到 ID,保留请求时间、模型及已有请求标识,到用量日志查看可取得的记录,必要时联系支持核查。没有找到记录也不足以证明请求未被上游接受;本文没有核实可用于安全重放生成 POST 的幂等键,不建议只凭提示词相同就自动重提。

拿到 URL 后,只恢复下载

将自己已收到的完整结果 URL 存入私密环境变量 IMAGE_URL,在新目录下载到临时文件:

umask 077
mkdir -p image-recovery
curl --fail --silent --show-error --location   --connect-timeout 15 --max-time 180   --output image-recovery/image.part   "$IMAGE_URL"

这里没有附加模型 API Key。结果地址可能属于独立 CDN 或对象存储,不要把 API 的 Authorization 头复制过去;下载链接若带签名参数,完整链接本身也应私密保存。

出现问题时,按下载这一步处理:

  • 超时、临时网络中断:保留原任务和结果,只对下载 GET 做有上限的重试;未完成的 .part 不进入发布目录。
  • 403/404:核对完整链接、有效期或资源状态,查看能否从原任务重新取得可用结果;不要假定每个 403 都是过期,也不要假定查询一定会刷新链接。
  • 浏览器能打开,程序取不到:比较运行机器、重定向与网络路径。运行在服务器的任务不会自动获得你浏览器的登录状态。
  • HTTP 200 但打不开:查看 Content-Type 和实际内容,可能下载到错误页。仅改成 .png 后缀不能让它成为图片。

本文没有查到可统一套用的本站结果 URL 有效时长,因此不给“一小时内下载”这类固定承诺。需要长期使用的成品,及时复制到自己控制的存储,并单独确认其访问权限和保留时间。

Base64 保存示例:先验证,再写成品

下面的脚本只处理已保存的同步 Images 响应中的第一张 PNG、JPEG 或 WebP,不提交生成请求,也不解析异步任务响应。处理多张时应逐项扩展,并分别记录完成状态。GIF、SVG 等输出要用适合它们的校验方式,不在此例范围内。

在本地 Python 3 环境安装 Pillow:

python3 -m pip install Pillow

保存为 save-b64.py:

import base64
import io
import json
import os
import sys
import tempfile
from pathlib import Path

from PIL import Image

response = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
raw = base64.b64decode(response["data"][0]["b64_json"], validate=True)
with Image.open(io.BytesIO(raw)) as image:
    suffix = {"PNG": ".png", "JPEG": ".jpg", "WEBP": ".webp"}[image.format]
    image.verify()
with Image.open(io.BytesIO(raw)) as image:
    image.load()

folder = Path(sys.argv[2])
folder.mkdir(parents=True, exist_ok=True)
destination = folder / ("image" + suffix)
with tempfile.NamedTemporaryFile(dir=folder, delete=False) as temporary:
    temporary.write(raw)
    temporary.flush()
    os.fsync(temporary.fileno())
    temporary_path = Path(temporary.name)
try:
    os.link(temporary_path, destination)
finally:
    temporary_path.unlink()
print(destination)

将已有响应存为私密的 result.json,执行:

python3 save-b64.py result.json image-recovery

脚本严格检查 Base64,再用 Pillow 验证并完整解码,按实际格式选择后缀。验证通过后写入同目录的私有临时文件,最后用硬链接发布成品名;目标已存在会失败,不覆盖旧文件。这要求该目录所在文件系统支持硬链接,适合普通本地磁盘,不是对象存储上传示例。

本文用临时生成的 PNG、JPEG、WebP,以及非法 Base64、损坏图片和已存在目标文件检查过这段脚本。没有为这些检查调用付费模型,也没有模拟出一笔“避免重复收费”的金额。

文件发布前依次检查响应、严格解码、完整打开、私有临时文件与成品落盘,失败不进入生成接口

什么情况下才需要重新生成

先明确旧结果无法恢复,或新需求确实要一张新图,再决定是否重新提交。提示词相同不代表同一个任务,服务也不一定把重复请求当成一次。

发布前至少确认:文件能完整打开、格式与后缀一致、尺寸适合用途、上传后的文件可读,最后再更新文章或消息中的地址。需要保持原始输出时,校验后保存原字节,不要在“下载”过程中顺手转码、压缩或裁切。

用本站服务时,费用疑问按账户与计费说明核对实际请求和结算;客户端显示下载失败,不能单独决定是否应有费用。要回看一次任务,还可以按 T3 Code 用量与账单核对方法分清客户端记录和服务记录。开始接入的读者则先看图片 API 入门。

相关文章

GPT-image / 多模态生图 API 新手教程
GPT ImageImage GenerationMultimodal

GPT-image / 多模态生图 API 新手教程

从文本生图、参考图编辑、Base URL、b64_json 保存到错误排查,带你用 OpenAI-compatible API 搭好第一条可复用的图片生成工作流。

用 Nbility 跑通你的 Agent 工作流

获取 API Key,统一接入 OpenAI 兼容模型和开发工具。

获取 API Key