最近我一直在找可以直接用 API 產圖,而且成本不要太誇張的服務
最後又回頭看了一下 Cloudflare Workers AI
Cloudflare 其實不只有文字模型,它也有提供圖片生成模型,其中一個就是
@cf/black-forest-labs/flux-1-schnell
(這張就是透過 black-forest-labs/flux-1-schnell 生成的)
FLUX.1 Schnell 是 Black Forest Labs 的文字產圖模型,目前 Cloudflare 直接 Hosted,所以目前還免費,免費就是要給他用一下
對我來說比較重要的是,它可以直接透過 REST API 呼叫,所以 C# 要串其實非常簡單
1.先到 Cloudflare 拿 API Token
登入 Cloudflare Dashboard 後,進入 Workers AI
在 REST API 的設定頁面可以取得兩個之後會用到的東西
Account ID //通常是一串英數字混合
API Token // 開頭是 直接建立 Workers AI 專用的 API Token 即可
我不建議直接使用 Global API Key,API Token 權限比較好控制,也比較適合實際專案使用
建立完成後,把 Account ID 與 API Token 記下來
2.使用 FLUX.1 Schnell
這次使用的模型是 @cf/black-forest-labs/flux-1-schnell
Cloudflare Workers AI 的 API Endpoint 格式如下:
https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/run/{MODEL}所以 FLUX.1 Schnell 會是
https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/run/@cf/black-forest-labs/flux-1-schnell主要會用到的參數有 prompt 是必要的。
steps 代表生成步數,預設值是 4。
這顆模型目前我實測時如果傳入不支援的參數,例如 seed,會直接得到 400,所以這篇範例先只保留目前確定可用的參數。
下面這份是我整理過的完整版本
除了可以直接產圖,我也加了一些基本檢查,像是 Prompt 長度、Steps 上限、Timeout,以及 Cloudflare 錯誤訊息輸出
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace CFImageTest
{
internal class Program
{
// Cloudflare Workers AI 使用的模型名稱。
// 這裡使用 FLUX.1 Schnell,屬於速度較快的文字產圖模型。
private const string Model = "@cf/black-forest-labs/flux-1-schnell";
// Prompt 最大允許長度。
// 避免送出過長的 Prompt 而造成 API 錯誤。
private const int MaxPromptLength = 2048;
// FLUX.1 Schnell 可接受的最大生成步數。
// steps 越高會增加生成計算量與回應時間。
private const int MaxSteps = 8;
// 沒有從命令列傳入 Prompt 時使用的預設 Prompt。
private const string DefaultPrompt = """
A realistic young Taiwanese female C# software engineer sitting naturally at a desk and working on a laptop,
young Taiwanese woman in her 20s,
ponytail hairstyle,
wearing a simple elegant short dress,
professional and attractive appearance,
full upper body clearly visible,
head, face, neck, shoulders, torso, arms and hands fully connected and anatomically correct,
natural seated posture,
both hands resting naturally on the keyboard,
professional modern software engineering workspace,
one main monitor and one secondary monitor positioned behind and beside her,
monitors must not block or intersect the woman's head, face, neck, shoulders or body,
computer screens visible only as background elements,
clean modern office,
soft professional lighting,
realistic photography,
natural skin texture,
highly detailed,
balanced composition,
correct human anatomy,
correct body proportions,
realistic hands and fingers,
no duplicated body parts,
no missing body parts,
no detached limbs,
no floating head,
no extra arms,
no extra hands,
no malformed hands,
no distorted body,
no overlapping body parts,
no monitor intersecting the person,
no readable text,
no logos
""";
static async Task<int> Main(string[] args)
{
// Cloudflare Account ID。
var accountId = "YOUR_ACCOUNT_ID";
// Workers AI 專用 API Token。
var apiToken = "YOUR_API_TOKEN";
if (string.IsNullOrWhiteSpace(accountId) ||
string.IsNullOrWhiteSpace(apiToken))
{
Console.Error.WriteLine("請設定 CF_ACCOUNT_ID 與 CF_API_TOKEN。");
return 1;
}
var prompt =
args.Length > 0 &&
!string.IsNullOrWhiteSpace(args[0])
? args[0]
: DefaultPrompt;
var outputPath =
args.Length > 1
? args[1]
: "flux-output-" +
DateTime.Now.ToString("yyMMddHHmmss") +
".jpg";
if (prompt.Length > MaxPromptLength)
{
Console.Error.WriteLine(
$"Prompt 長度 {prompt.Length} 超過上限 {MaxPromptLength} 字元。");
return 1;
}
// 從環境變數取得 Steps,沒有設定時預設為 4。
var steps =
int.TryParse(
Environment.GetEnvironmentVariable("CF_STEPS"),
out var s)
? s
: 4;
if (steps is < 1 or > MaxSteps)
{
Console.Error.WriteLine(
$"CF_STEPS 必須介於 1 到 {MaxSteps}。");
return 1;
}
var apiUrl =
$"https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/run/{Model}";
using var client = new HttpClient
{
Timeout = TimeSpan.FromMinutes(2)
};
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiToken);
var request = new
{
prompt,
steps
};
using var content = new StringContent(
JsonSerializer.Serialize(request),
Encoding.UTF8,
"application/json");
Console.WriteLine($"Model: {Model}");
Console.WriteLine($"Steps: {steps}");
Console.WriteLine("產圖中...");
using var response =
await client.PostAsync(apiUrl, content);
var responseJson =
await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
Console.Error.WriteLine(
$"Cloudflare 回傳錯誤:{(int)response.StatusCode} {response.ReasonPhrase}");
Console.Error.WriteLine(responseJson);
return 1;
}
using var document =
JsonDocument.Parse(responseJson);
// Cloudflare 回傳的圖片內容在 result.image,格式為 Base64。
var base64 =
document.RootElement
.GetProperty("result")
.GetProperty("image")
.GetString();
if (string.IsNullOrEmpty(base64))
{
Console.Error.WriteLine("Cloudflare 沒有回傳圖片");
Console.Error.WriteLine(responseJson);
return 1;
}
var imageBytes =
Convert.FromBase64String(base64);
await File.WriteAllBytesAsync(
outputPath,
imageBytes);
Console.WriteLine(
$"圖片產生完成:{Path.GetFullPath(outputPath)} ({imageBytes.Length:N0} bytes)");
return 0;
}
}
}這次我測試的人物 prompt 是一位台灣年輕女性 C# 軟體工程師,綁馬尾,在辦公桌前工作。
Cloudflare Workers AI 比較吸引我的地方,不一定是模型本身有多強,而是它非常容易塞進既有系統且免費 :)
對 C# 來說,基本上就是一個普通的 REST API
原本的 ASP.NET、Web API、Background Service 或排程程式,都可以直接接
像 Blog 自動產圖這種需求就很適合。文章建立時先讓 AI 產生圖片 Prompt,再交給 FLUX.1 Schnell 產圖,最後把圖片存起來
簡單做個小結論
FLUX.1 Schnell 不一定是現在最強的圖片模型,而且偏弱,但是勝在免費
但如果只是拿來做 Blog 封面、文章配圖、網站素材或批次自動產圖,我覺得已經非常夠用
另外我自己的建議是,Prompt 盡量不要要求模型產生太多文字
因為目前大多數圖片模型在文字這一塊其實還是不夠穩,常常會出現拼錯、亂字、變形字,甚至產出看起來像字但根本不能讀的內容
所以如果只是要做配圖、封面圖或情境圖,我通常會盡量避免讓模型直接產生文字
真的需要標題、按鈕、海報文案或畫面上的可讀資訊,後製自己補上去,通常會比較準
人物圖片也是一樣,畫面越複雜,尤其是多螢幕、手部、桌面物件或多人交錯,越容易增加人體結構出錯的機率
這也是西方的模型對於中文下指令常常不是很好,所以建議通常都用文字模型請他寫出說要給 flux-1-schnell 來生成圖片
一開始我都亂打就會出現靈異照片,後來我還是請 GPT 幫我生成 Prompt
他會給你比較可以預期的結果,果然還是要用 AI 操控 AI 阿 ..
---
The bug existed in all possible states.
Until I ran the code.

