using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// ====== 配置 ======
var apiKey = "sk-xxxxxxxxxxxx"; // 换成你的apikey
var baseUrl = "https://xinghuapi.com/v1/chat/completions";
var model = "gemini-3-pro-image-preview"; // 以 /v1/models 为准
var prompt = "生成一张:纯色背景的可爱小狐狸头像,卡通风格,高清";
using var http = new HttpClient { Timeout = TimeSpan.FromMinutes(10) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
var requestBody = new
{
model,
messages = new object[]
{
new { role = "user", content = prompt }
}
};
var requestJson = JsonSerializer.Serialize(requestBody);
using var reqContent = new StringContent(requestJson, Encoding.UTF8, "application/json");
Console.WriteLine("🚀 请求生图中...");
using var resp = await http.PostAsync(baseUrl, reqContent);
var responseText = await resp.Content.ReadAsStringAsync();
Console.WriteLine($"📥 HTTP {(int)resp.StatusCode} {resp.StatusCode}");
if (!resp.IsSuccessStatusCode)
{
Console.WriteLine("❌ 响应:");
Console.WriteLine(responseText);
return;
}
// 只预览一点点,避免刷屏
Console.WriteLine("🧾 响应前200字符预览:");
Console.WriteLine(responseText.Length > 200 ? responseText.Substring(0, 200) + "..." : responseText);
// ====== 取出 content 字符串(你的情况就是 choices[0].message.content 是 string)======
string? contentStr = ExtractContentString(responseText);
if (string.IsNullOrWhiteSpace(contentStr))
{
Console.WriteLine("⚠️ 没取到 message.content");
return;
}
// ====== 从 Markdown 或 data url 里提取 mime + base64 ======
var (mime, base64) = ExtractDataUrlBase64(contentStr);
if (string.IsNullOrWhiteSpace(base64))
{
Console.WriteLine("⚠️ 没在 content 中找到 data:image/...;base64,xxxx");
Console.WriteLine("content预览:");
Console.WriteLine(contentStr.Length > 300 ? contentStr.Substring(0, 300) + "..." : contentStr);
return;
}
// 清理换行
base64 = base64.Replace("\r", "").Replace("\n", "").Trim();
// 根据 mime 决定扩展名
var ext = mime switch
{
"image/jpeg" => ".jpg",
"image/jpg" => ".jpg",
"image/png" => ".png",
"image/webp" => ".webp",
_ => ".png"
};
// 保存
var bytes = Convert.FromBase64String(base64);
var fileName = $"output_{DateTime.Now:yyyyMMdd_HHmmss}{ext}";
File.WriteAllBytes(fileName, bytes);
Console.WriteLine($"✅ 图片已保存:{Path.GetFullPath(fileName)}");
}
// 兼容:content 是 string / content 是 array(取第一个 string 或 object.text)
static string? ExtractContentString(string json)
{
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
// choices[0].message.content
var content = root.GetProperty("choices")[0].GetProperty("message").GetProperty("content");
if (content.ValueKind == JsonValueKind.String)
return content.GetString();
if (content.ValueKind == JsonValueKind.Array && content.GetArrayLength() > 0)
{
var first = content[0];
if (first.ValueKind == JsonValueKind.String) return first.GetString();
if (first.ValueKind == JsonValueKind.Object)
{
if (first.TryGetProperty("text", out var t) && t.ValueKind == JsonValueKind.String) return t.GetString();
if (first.TryGetProperty("data", out var d) && d.ValueKind == JsonValueKind.String) return d.GetString();
}
}
return null;
}
// 从以下格式提取:
// 1) "data:image/jpeg;base64,AAAA..."
// 2) ""
static (string mime, string base64) ExtractDataUrlBase64(string s)
{
// 先从 markdown 里抠括号
// 
var mdMatch = Regex.Match(s, @"!\[.*?\]\((data:image\/[a-zA-Z0-9\+\-\.]+;base64,[A-Za-z0-9\/\+\=\r\n]+)\)");
if (mdMatch.Success)
{
s = mdMatch.Groups[1].Value;
}
// 再匹配 data url
var m = Regex.Match(s, @"data:(image\/[a-zA-Z0-9\+\-\.]+);base64,([A-Za-z0-9\/\+\=\r\n]+)");
if (!m.Success) return ("", "");
var mime = m.Groups[1].Value;
var base64 = m.Groups[2].Value;
return (mime, base64);
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// ====== 1) 配置(改这里) ======
var apiKey = "sk-xxxxxxxxxxxxxxxxxxxx"; // 换成你的
var baseUrl = "https://xinghuapi.com/v1/chat/completions";
// 模型名:以你 /v1/models 返回为准(常见:gemini-3-pro-image-preview / xh|gemini-3-pro-image-preview)
var model = "gemini-3-pro-image-preview";
// 参考图本地路径
var inputImagePath = @"D:\CursorProject\input.jpg"; // 改成你的图片路径
// 编辑指令
var instruction = "请在保持主体不变的前提下,把背景改成粉色渐变,并加一些可爱的星星点缀。整体更清晰,偏二次元插画风。只输出图片,不要输出文字。";
if (!File.Exists(inputImagePath))
{
Console.WriteLine("❌ 找不到参考图文件:" + inputImagePath);
return;
}
// ====== 2) 读取图片 → data URL ======
var imgBytes = await File.ReadAllBytesAsync(inputImagePath);
var imgBase64 = Convert.ToBase64String(imgBytes);
var mime = GuessMimeType(inputImagePath);
var dataUrl = $"data:{mime};base64,{imgBase64}";
// ====== 3) HttpClient ======
using var http = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10) // 生图/图生图建议拉长
};
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
// ====== 4) OpenAI 兼容 chat/completions 请求体(图+文) ======
// 关键:content 用数组,包含 text + image_url(data URL)
var requestBody = new
{
model = model,
messages = new object[]
{
new
{
role = "user",
content = new object[]
{
new { type = "text", text = instruction },
new { type = "image_url", image_url = new { url = dataUrl } }
}
}
},
temperature = 0.7
};
var json = JsonSerializer.Serialize(requestBody);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
Console.WriteLine("🚀 OpenAI兼容 图生图请求中...");
using var resp = await http.PostAsync(baseUrl, content);
var respText = await resp.Content.ReadAsStringAsync();
Console.WriteLine($"📥 HTTP {(int)resp.StatusCode} {resp.StatusCode}");
if (!resp.IsSuccessStatusCode)
{
Console.WriteLine("❌ 请求失败,响应如下:");
Console.WriteLine(respText);
return;
}
// 不刷屏,只预览一小段
Console.WriteLine("🧾 响应前200字符预览:");
Console.WriteLine(respText.Length > 200 ? respText.Substring(0, 200) + "..." : respText);
// ====== 5) 从响应中提取图片 base64 / data URL ======
var imageInfo = TryExtractOpenAIImage(respText);
if (imageInfo.base64 == null)
{
Console.WriteLine("⚠️ 没找到图片数据(可能模型只返回文字 / 或该兼容层未开启图生图)。");
Console.WriteLine("📄 完整响应如下(用于排查):");
Console.WriteLine(respText);
return;
}
// 保存
var ext = (imageInfo.mime ?? "").ToLowerInvariant() switch
{
"image/jpeg" => ".jpg",
"image/jpg" => ".jpg",
"image/png" => ".png",
"image/webp" => ".webp",
_ => ".png"
};
var outFile = $"edited_openai_{DateTime.Now:yyyyMMdd_HHmmss}{ext}";
var outBytes = Convert.FromBase64String(imageInfo.base64);
await File.WriteAllBytesAsync(outFile, outBytes);
Console.WriteLine($"✅ 已保存编辑后图片:{Path.GetFullPath(outFile)}");
}
// ========== 工具函数 ==========
static string GuessMimeType(string path)
{
var ext = Path.GetExtension(path).ToLowerInvariant();
return ext switch
{
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".webp" => "image/webp",
_ => "image/jpeg"
};
}
/// <summary>
/// 尝试从 OpenAI 兼容 chat/completions 的响应里提取图片。
/// 兼容三类常见返回:
/// 1) choices[0].message.content 是 string: ""
/// 2) choices[0].message.content 是 array: [{image_base64:"..."}, ...] 或 {b64_json:"..."}
/// 3) data[0].b64_json(少数实现)
/// </summary>
static (string? mime, string? base64) TryExtractOpenAIImage(string json)
{
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
// A) choices[0].message.content 是 string(你的星狐云常见:Markdown 包裹 data URL)
if (TryGet(root, out var contentEl, "choices", 0, "message", "content"))
{
if (contentEl.ValueKind == JsonValueKind.String)
{
var s = contentEl.GetString() ?? "";
return ExtractFromMarkdownOrDataUrl(s);
}
// B) content 是 array:找 image_base64 / b64_json / text(data:url)
if (contentEl.ValueKind == JsonValueKind.Array)
{
foreach (var part in contentEl.EnumerateArray())
{
if (part.ValueKind == JsonValueKind.Object)
{
if (part.TryGetProperty("image_base64", out var ib) && ib.ValueKind == JsonValueKind.String)
return (null, CleanBase64(ib.GetString()));
if (part.TryGetProperty("b64_json", out var b64) && b64.ValueKind == JsonValueKind.String)
return (null, CleanBase64(b64.GetString()));
if (part.TryGetProperty("text", out var t) && t.ValueKind == JsonValueKind.String)
{
var r = ExtractFromMarkdownOrDataUrl(t.GetString() ?? "");
if (r.base64 != null) return r;
}
if (part.TryGetProperty("data", out var d) && d.ValueKind == JsonValueKind.String)
{
var r = ExtractFromMarkdownOrDataUrl(d.GetString() ?? "");
if (r.base64 != null) return r;
}
}
if (part.ValueKind == JsonValueKind.String)
{
var r = ExtractFromMarkdownOrDataUrl(part.GetString() ?? "");
if (r.base64 != null) return r;
}
}
}
}
// C) 兼容 images 风格:data[0].b64_json
if (TryGet(root, out var b64El, "data", 0, "b64_json") && b64El.ValueKind == JsonValueKind.String)
return (null, CleanBase64(b64El.GetString()));
return (null, null);
}
static (string? mime, string? base64) ExtractFromMarkdownOrDataUrl(string s)
{
if (string.IsNullOrWhiteSpace(s)) return (null, null);
// 1) Markdown: 
var md = Regex.Match(s, @"!\[.*?\]\((data:image\/[a-zA-Z0-9\+\-\.]+;base64,[A-Za-z0-9\/\+\=\r\n]+)\)");
if (md.Success) s = md.Groups[1].Value;
// 2) data URL: data:image/png;base64,xxx
var m = Regex.Match(s, @"data:(image\/[a-zA-Z0-9\+\-\.]+);base64,([A-Za-z0-9\/\+\=\r\n]+)");
if (!m.Success) return (null, null);
var mime = m.Groups[1].Value;
var base64 = CleanBase64(m.Groups[2].Value);
return (mime, base64);
}
static string? CleanBase64(string? b64)
{
if (string.IsNullOrWhiteSpace(b64)) return null;
return b64.Replace("\r", "").Replace("\n", "").Trim();
}
static bool TryGet(JsonElement root, out JsonElement value, params object[] path)
{
value = root;
foreach (var p in path)
{
if (p is string key)
{
if (value.ValueKind != JsonValueKind.Object || !value.TryGetProperty(key, out value))
return false;
}
else if (p is int idx)
{
if (value.ValueKind != JsonValueKind.Array || value.GetArrayLength() <= idx)
return false;
value = value[idx];
}
else return false;
}
return true;
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var apiKey = "sk-xxxxxxx"; // 换成你的
var url = "https://xinghuapi.com/v1beta/models/gemini-3-pro-image-preview:generateContent";
using var http = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10)
};
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
// ===== Gemini 原生请求体 =====
var body = new
{
contents = new object[]
{
new {
role = "user",
parts = new object[]
{
new { text = "生成一张:纯色背景的可爱小狐狸头像,卡通风格,高清" }
}
}
}
};
var json = JsonSerializer.Serialize(body);
var content = new StringContent(json, Encoding.UTF8, "application/json");
Console.WriteLine("🚀 Gemini generateContent 请求中...");
var resp = await http.PostAsync(url, content);
var respText = await resp.Content.ReadAsStringAsync();
Console.WriteLine($"📥 HTTP {(int)resp.StatusCode} {resp.StatusCode}");
if (!resp.IsSuccessStatusCode)
{
Console.WriteLine("❌ 请求失败:");
Console.WriteLine(respText);
return;
}
// ===== 解析 Gemini 返回 =====
using var doc = JsonDocument.Parse(respText);
var base64 =
doc.RootElement
.GetProperty("candidates")[0]
.GetProperty("content")
.GetProperty("parts")[0]
.GetProperty("inlineData")
.GetProperty("data")
.GetString();
var mime =
doc.RootElement
.GetProperty("candidates")[0]
.GetProperty("content")
.GetProperty("parts")[0]
.GetProperty("inlineData")
.GetProperty("mimeType")
.GetString();
var bytes = Convert.FromBase64String(base64!);
var ext = mime switch
{
"image/jpeg" => ".jpg",
"image/png" => ".png",
"image/webp" => ".webp",
_ => ".png"
};
var file = $"gemini_native_{DateTime.Now:yyyyMMdd_HHmmss}{ext}";
File.WriteAllBytes(file, bytes);
Console.WriteLine($"✅ 图片已保存:{Path.GetFullPath(file)}");
}
}
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// ====== 1) 配置(改这里) ======
var apiKey = "sk-xxxxxxxx"; // 换成你的
var url = "https://xinghuapi.com/v1beta/models/gemini-3-pro-image-preview:generateContent";
// 参考图路径(本地文件)
var inputImagePath = @"D:\CursorProject\参考图.jpg"; // 改成你的图片路径
// 修改指令(越具体越好)
var editInstruction =
"请在保持人物主体不变的前提下,把背景改成粉色渐变,并加一些可爱的星星点缀。画面更清晰,整体偏二次元插画风。只输出图片,不要输出文字。";
// ====== 2) 读取参考图并转 base64 ======
if (!File.Exists(inputImagePath))
{
Console.WriteLine("❌ 找不到参考图文件:" + inputImagePath);
return;
}
var imageBytes = await File.ReadAllBytesAsync(inputImagePath);
var imageBase64 = Convert.ToBase64String(imageBytes);
var mimeType = GuessMimeType(inputImagePath); // 根据扩展名猜测
// ====== 3) HttpClient ======
using var http = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10)
};
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
// ====== 4) Gemini 原生请求体:parts 同时包含「图片 + 文本指令」 ======
var body = new
{
contents = new object[]
{
new
{
role = "user",
parts = new object[]
{
// 先放参考图
new
{
inlineData = new
{
mimeType = mimeType,
data = imageBase64
}
},
// 再放编辑指令
new { text = editInstruction }
}
}
}
};
var json = JsonSerializer.Serialize(body);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
Console.WriteLine("🚀 Gemini 图生图(参考图编辑)请求中...");
using var resp = await http.PostAsync(url, content);
var respText = await resp.Content.ReadAsStringAsync();
Console.WriteLine($"📥 HTTP {(int)resp.StatusCode} {resp.StatusCode}");
if (!resp.IsSuccessStatusCode)
{
Console.WriteLine("❌ 请求失败,响应如下:");
Console.WriteLine(respText);
return;
}
// ====== 5) 解析返回:遍历 parts 找 inlineData(图片) ======
try
{
using var doc = JsonDocument.Parse(respText);
var parts = doc.RootElement
.GetProperty("candidates")[0]
.GetProperty("content")
.GetProperty("parts");
string? outBase64 = null;
string? outMime = null;
var textTips = new StringBuilder();
foreach (var part in parts.EnumerateArray())
{
// 图片输出
if (part.TryGetProperty("inlineData", out var inline))
{
if (inline.TryGetProperty("data", out var d) && d.ValueKind == JsonValueKind.String)
outBase64 = d.GetString();
if (inline.TryGetProperty("mimeType", out var m) && m.ValueKind == JsonValueKind.String)
outMime = m.GetString();
if (!string.IsNullOrWhiteSpace(outBase64))
break;
}
// 有时会返回 text 提示
if (part.TryGetProperty("text", out var t) && t.ValueKind == JsonValueKind.String)
{
var s = t.GetString();
if (!string.IsNullOrWhiteSpace(s))
textTips.AppendLine(s);
}
}
if (string.IsNullOrWhiteSpace(outBase64))
{
Console.WriteLine("⚠️ 本次响应没有返回图片 inlineData。可能返回了文本/安全提示/模型拒绝等。");
if (textTips.Length > 0)
{
Console.WriteLine("📝 返回文本:");
Console.WriteLine(textTips.ToString());
}
else
{
Console.WriteLine("(没有 text 提示)");
}
return;
}
outBase64 = outBase64.Replace("\r", "").Replace("\n", "").Trim();
var outBytes = Convert.FromBase64String(outBase64);
var ext = (outMime ?? "").ToLowerInvariant() switch
{
"image/jpeg" => ".jpg",
"image/jpg" => ".jpg",
"image/png" => ".png",
"image/webp" => ".webp",
_ => ".png"
};
var outFile = $"edited_{DateTime.Now:yyyyMMdd_HHmmss}{ext}";
await File.WriteAllBytesAsync(outFile, outBytes);
Console.WriteLine($"✅ 已保存编辑后图片:{Path.GetFullPath(outFile)}");
}
catch (Exception ex)
{
Console.WriteLine("💥 解析失败:");
Console.WriteLine(ex);
Console.WriteLine("📄 原始响应(用于排查):");
Console.WriteLine(respText);
}
}
static string GuessMimeType(string path)
{
var ext = Path.GetExtension(path).ToLowerInvariant();
return ext switch
{
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".webp" => "image/webp",
_ => "image/jpeg" // 不确定就用 jpeg
};
}
}