利用闲置的any token 畅快使用gpt-image-2生图

2026-04-29 08:213阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

能直接用的地址,只用填上你的key即可。
AI 图片生成器
效果图
image2348×1878 410 KB

下面是教程以及源码
在你的claude code 中安装如下的mcp
“mcpServers”: {
“edgeone-pages-mcp-server”: {
“url”: “https://mcp-on-edge.edgeone.app/mcp-server”
}
}
ps:使用cc desktop的需要重启desktop,才能使用部署好的edgeone mcp。
接下来就是你按照如下代码生成h5网页并使用mcp发布到edgeone,服务端会给你返回一个地址。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI 图片生成器</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); min-height: 100vh; color: #e0e0e0; } .container { max-width: 760px; margin: 0 auto; padding: 40px 20px; } h1 { text-align: center; font-size: 2em; margin-bottom: 8px; background: linear-gradient(90deg, #667eea, #764ba2, #f093fb); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .subtitle { text-align: center; color: #888; margin-bottom: 36px; font-size: 0.95em; } .card { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 28px; margin-bottom: 24px; backdrop-filter: blur(10px); } label { display: block; font-size: 0.85em; color: #aaa; margin-bottom: 6px; font-weight: 500; } input, textarea, select { width: 100%; padding: 12px 14px; border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; background: rgba(0,0,0,0.3); color: #f0f0f0; font-size: 0.95em; margin-bottom: 18px; transition: border-color 0.2s; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 80px; } .row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .btn-generate { width: 100%; padding: 14px; border: none; border-radius: 12px; font-size: 1.05em; font-weight: 600; cursor: pointer; background: linear-gradient(135deg, #667eea, #764ba2); color: white; transition: transform 0.15s, box-shadow 0.2s, opacity 0.2s; margin-top: 4px; } .btn-generate:hover { transform: translateY(-1px); box-shadow: 0 6px 24px rgba(102,126,234,0.4); } .btn-generate:active { transform: translateY(0); } .btn-generate:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none; } .result-area { text-align: center; min-height: 120px; display: flex; flex-direction: column; align-items: center; justify-content: center; } .result-area img { max-width: 100%; max-height: 520px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.4); cursor: pointer; transition: transform 0.2s; } .result-area img:hover { transform: scale(1.01); } .result-actions { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; justify-content: center; } .btn-action { padding: 10px 28px; border: 1px solid rgba(255,255,255,0.2); border-radius: 10px; background: rgba(255,255,255,0.08); color: #e0e0e0; cursor: pointer; font-size: 0.9em; transition: background 0.2s; } .btn-action:hover { background: rgba(255,255,255,0.15); } .spinner { width: 48px; height: 48px; border: 4px solid rgba(255,255,255,0.1); border-top-color: #667eea; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .loading-text { margin-top: 16px; color: #aaa; font-size: 0.9em; } .placeholder-icon { font-size: 3em; margin-bottom: 12px; opacity: 0.3; } .placeholder-text { color: #555; font-size: 0.9em; } .error-msg { color: #ff6b6b; background: rgba(255,107,107,0.1); border: 1px solid rgba(255,107,107,0.2); border-radius: 10px; padding: 14px; margin-top: 12px; font-size: 0.9em; text-align: left; word-break: break-all; } .size-selector { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; } .size-option { padding: 8px 16px; border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; background: rgba(0,0,0,0.2); color: #ccc; cursor: pointer; font-size: 0.85em; transition: all 0.2s; } .size-option:hover { border-color: #667eea; color: #fff; } .size-option.active { border-color: #667eea; background: rgba(102,126,234,0.2); color: #fff; } /* 历史记录区域 */ .history-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; } .history-header h2 { font-size: 1.1em; color: #aaa; } .history-count { font-size: 0.8em; color: #666; background: rgba(255,255,255,0.05); padding: 4px 12px; border-radius: 20px; } .btn-clear { padding: 6px 16px; border: 1px solid rgba(255,100,100,0.3); border-radius: 8px; background: rgba(255,100,100,0.08); color: #ff8888; cursor: pointer; font-size: 0.8em; transition: all 0.2s; } .btn-clear:hover { background: rgba(255,100,100,0.15); border-color: rgba(255,100,100,0.5); } .history-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; } .history-item { position: relative; border-radius: 12px; overflow: hidden; cursor: pointer; border: 2px solid transparent; transition: all 0.2s; aspect-ratio: 1; background: rgba(0,0,0,0.3); } .history-item:hover { border-color: #667eea; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.4); } .history-item.active { border-color: #f093fb; box-shadow: 0 0 0 2px rgba(240,147,251,0.3); } .history-item img { width: 100%; height: 100%; object-fit: cover; } .history-item .item-meta { position: absolute; bottom: 0; left: 0; right: 0; padding: 6px 8px; background: linear-gradient(transparent, rgba(0,0,0,0.8)); font-size: 0.7em; color: #ccc; display: flex; justify-content: space-between; align-items: center; } .history-item .item-index { position: absolute; top: 6px; left: 6px; background: rgba(0,0,0,0.6); color: #aaa; font-size: 0.7em; padding: 2px 8px; border-radius: 10px; } .history-item .btn-del { background: none; border: none; color: #ff6b6b; cursor: pointer; font-size: 1em; padding: 0 4px; opacity: 0; transition: opacity 0.2s; } .history-item:hover .btn-del { opacity: 1; } .history-empty { text-align: center; padding: 40px 20px; color: #555; } /* 全屏预览 */ .lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.92); z-index: 1000; justify-content: center; align-items: center; cursor: zoom-out; } .lightbox.show { display: flex; } .lightbox img { max-width: 95vw; max-height: 92vh; border-radius: 8px; box-shadow: 0 12px 48px rgba(0,0,0,0.6); } .lightbox-close { position: fixed; top: 20px; right: 24px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: white; width: 40px; height: 40px; border-radius: 50%; font-size: 1.2em; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.2s; } .lightbox-close:hover { background: rgba(255,255,255,0.2); } footer { text-align: center; color: #444; font-size: 0.8em; margin-top: 40px; } footer a { color: #667eea; text-decoration: none; } </style> </head> <body> <div class="container"> <h1>🎨 AI 图片生成器</h1> <p class="subtitle">基于 OpenAI Responses API,在浏览器中直接生成图片</p> <div class="card"> <div class="row"> <div> <label>Base URL</label> <input type="text" id="baseUrl" placeholder="https://api.openai.com/v1" value="https://anyrouter.top/v1"> </div> <div> <label>Model</label> <input type="text" id="model" placeholder="gpt-5.4" value="gpt-5.3-codex"> </div> </div> <label>API Key</label> <input type="password" id="apiKey" placeholder="sk-...(密钥仅在浏览器本地使用,不会上传)"> <label>图片尺寸</label> <div class="size-selector" id="sizeSelector"> <div class="size-option" data-size="1024x1024">1024×1024</div> <div class="size-option active" data-size="1024x1536">1024×1536</div> <div class="size-option" data-size="1536x1024">1536×1024</div> <div class="size-option" data-size="auto">Auto</div> </div> <label>提示词</label> <textarea id="prompt" placeholder="描述你想生成的图片...">为我生成图中角色的绘制 Q 版的,LINE 风格的半身像表情包,注意头饰要正确 彩色手绘风格,使用 4x6 布局,涵盖各种各样的常用聊天语句,或是一些有关的娱乐 meme 其他需求:不要原图复制。所有标注为手写简体中文。 生成的图片需为 2K 分辨率 16:9</textarea> <button class="btn-generate" id="btnGenerate" onclick="generate()">✨ 生成图片</button> </div> <!-- 当前结果 --> <div class="card"> <div class="result-area" id="resultArea"> <div class="placeholder-icon">🖼️</div> <div class="placeholder-text">图片将显示在这里</div> </div> </div> <!-- 历史记录 --> <div class="card" id="historyCard"> <div class="history-header"> <h2>📚 历史记录 <span class="history-count" id="historyCount">0 张</span></h2> <button class="btn-clear" id="btnClear" onclick="clearHistory()">🗑️ 清空</button> </div> <div id="historyGrid" class="history-grid"> <div class="history-empty">还没有生成过图片</div> </div> </div> <footer> Powered by <a href="https://edgeone.ai" target="_blank">EdgeOne Pages</a> · API 调用由浏览器直连,密钥不会经过第三方 · 历史图片存储在浏览器 IndexedDB 中 </footer> </div> <!-- 全屏预览 --> <div class="lightbox" id="lightbox" onclick="closeLightbox()"> <button class="lightbox-close" onclick="closeLightbox()">✕</button> <img id="lightboxImg" src="" alt="Preview" /> </div> <script> let selectedSize = '1024x1536'; let history = []; let activeIndex = -1; const DB_NAME = 'AIImageGenerator'; const DB_VERSION = 1; const STORE_NAME = 'images'; // ── IndexedDB ── function openDB() { return new Promise((resolve, reject) => { const req = indexedDB.open(DB_NAME, DB_VERSION); req.onupgradeneeded = (e) => { const db = e.target.result; if (!db.objectStoreNames.contains(STORE_NAME)) { db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true }); } }; req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } async function saveToDB(item) { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); const req = store.add(item); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } async function loadAllFromDB() { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readonly'); const store = tx.objectStore(STORE_NAME); const req = store.getAll(); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } async function deleteFromDB(id) { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); const req = store.delete(id); req.onsuccess = () => resolve(); req.onerror = () => reject(req.error); }); } async function clearDB() { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); const req = store.clear(); req.onsuccess = () => resolve(); req.onerror = () => reject(req.error); }); } // ── 初始化 ── document.querySelectorAll('.size-option').forEach(el => { el.addEventListener('click', () => { document.querySelectorAll('.size-option').forEach(e => e.classList.remove('active')); el.classList.add('active'); selectedSize = el.dataset.size; }); }); async function init() { try { history = await loadAllFromDB(); renderHistory(); if (history.length > 0) { showImage(history.length - 1); } } catch (e) { console.warn('IndexedDB load failed:', e); } } init(); // ── 渲染历史 ── function renderHistory() { const grid = document.getElementById('historyGrid'); const count = document.getElementById('historyCount'); count.textContent = history.length + ' 张'; if (history.length === 0) { grid.innerHTML = '<div class="history-empty">还没有生成过图片</div>'; return; } // 按时间倒序显示(最新在前) const sorted = [...history].reverse(); grid.innerHTML = sorted.map((item, ri) => { const realIndex = history.length - 1 - ri; const time = new Date(item.timestamp).toLocaleString('zh-CN', { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }); const shortPrompt = item.prompt.length > 20 ? item.prompt.slice(0, 20) + '…' : item.prompt; return ` <div class="history-item ${realIndex === activeIndex ? 'active' : ''}" onclick="showImage(${realIndex})"> <span class="item-index">#${realIndex + 1}</span> <img src="data:image/png;base64,${item.base64}" alt="${shortPrompt}" loading="lazy" /> <div class="item-meta"> <span>${time}</span> <button class="btn-del" onclick="event.stopPropagation();deleteImage(${realIndex})" title="删除">✕</button> </div> </div> `; }).join(''); } function showImage(index) { if (index < 0 || index >= history.length) return; activeIndex = index; const item = history[index]; const area = document.getElementById('resultArea'); const shortPrompt = item.prompt.length > 60 ? item.prompt.slice(0, 60) + '…' : item.prompt; area.innerHTML = ` <img src="data:image/png;base64,${item.base64}" alt="Generated Image" onclick="openLightbox(${index})" title="点击查看大图" /> <div style="margin-top:10px;color:#888;font-size:0.8em;max-width:500px;text-align:center">${shortPrompt}</div> <div class="result-actions"> <button class="btn-action" onclick="downloadImage(${index})">💾 下载</button> <button class="btn-action" onclick="copyPrompt(${index})">📋 复制提示词</button> <button class="btn-action" onclick="openLightbox(${index})">🔍 大图</button> </div> `; renderHistory(); } // ── 生成 ── function setGenerating(on) { const btn = document.getElementById('btnGenerate'); const area = document.getElementById('resultArea'); btn.disabled = on; if (on) { area.innerHTML = '<div class="spinner"></div><div class="loading-text">正在生成图片,请稍候...</div>'; } } function showError(msg) { const area = document.getElementById('resultArea'); area.innerHTML = `<div class="placeholder-icon">⚠️</div><div class="error-msg">${msg}</div>`; } async function generate() { const apiKey = document.getElementById('apiKey').value.trim(); const baseUrl = document.getElementById('baseUrl').value.trim(); const model = document.getElementById('model').value.trim(); const prompt = document.getElementById('prompt').value.trim(); if (!apiKey) { showError('请输入 API Key'); return; } if (!baseUrl) { showError('请输入 Base URL'); return; } if (!prompt) { showError('请输入提示词'); return; } setGenerating(true); try { const url = baseUrl.replace(/\/+$/, '') + '/responses'; const body = { model: model || 'gpt-5.4', input: prompt, tools: [{ type: 'image_generation' }] }; if (selectedSize !== 'auto') { body.tools[0].size = selectedSize; } const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify(body) }); if (!resp.ok) { const errText = await resp.text(); let errMsg = `HTTP ${resp.status}`; try { errMsg += ': ' + JSON.parse(errText).error?.message || errText; } catch { errMsg += ': ' + errText.slice(0, 300); } throw new Error(errMsg); } const data = await resp.json(); const imageOutputs = (data.output || []).filter(o => o.type === 'image_generation_call'); if (!imageOutputs.length) { throw new Error('API 返回成功但未包含生成的图片。响应: ' + JSON.stringify(data).slice(0, 500)); } const base64 = imageOutputs[0].result; const item = { base64, prompt, model: model || 'gpt-5.4', size: selectedSize, timestamp: Date.now() }; // 存入 IndexedDB try { const id = await saveToDB(item); item.id = id; } catch (e) { console.warn('IndexedDB save failed, using memory only:', e); } history.push(item); showImage(history.length - 1); } catch (err) { showError(err.message); } finally { document.getElementById('btnGenerate').disabled = false; } } // ── 操作 ── function downloadImage(index) { if (index < 0 || index >= history.length) return; const item = history[index]; const a = document.createElement('a'); a.href = 'data:image/png;base64,' + item.base64; a.download = 'ai-image-' + new Date(item.timestamp).toISOString().slice(0,10) + '-' + (index + 1) + '.png'; a.click(); } function copyPrompt(index) { if (index < 0 || index >= history.length) return; navigator.clipboard.writeText(history[index].prompt).then(() => { const btn = event.target; const orig = btn.textContent; btn.textContent = '✅ 已复制'; setTimeout(() => btn.textContent = orig, 1500); }); } async function deleteImage(index) { if (index < 0 || index >= history.length) return; const item = history[index]; try { await deleteFromDB(item.id); } catch (e) { console.warn(e); } history.splice(index, 1); if (activeIndex === index) { activeIndex = history.length > 0 ? Math.min(index, history.length - 1) : -1; if (activeIndex >= 0) showImage(activeIndex); else document.getElementById('resultArea').innerHTML = '<div class="placeholder-icon">🖼️</div><div class="placeholder-text">图片将显示在这里</div>'; } else if (activeIndex > index) { activeIndex--; } renderHistory(); } async function clearHistory() { if (!confirm('确定清空所有历史记录?此操作不可撤销。')) return; try { await clearDB(); } catch (e) { console.warn(e); } history = []; activeIndex = -1; document.getElementById('resultArea').innerHTML = '<div class="placeholder-icon">🖼️</div><div class="placeholder-text">图片将显示在这里</div>'; renderHistory(); } // ── 全屏预览 ── function openLightbox(index) { if (index < 0 || index >= history.length) return; const img = document.getElementById('lightboxImg'); img.src = 'data:image/png;base64,' + history[index].base64; document.getElementById('lightbox').classList.add('show'); document.body.style.overflow = 'hidden'; } function closeLightbox() { document.getElementById('lightbox').classList.remove('show'); document.body.style.overflow = ''; } document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeLightbox(); }); // ── 保存/恢复设置 ── ['apiKey', 'baseUrl', 'model'].forEach(id => { const el = document.getElementById(id); const saved = localStorage.getItem('imggen_' + id); if (saved) el.value = saved; el.addEventListener('change', () => localStorage.setItem('imggen_' + id, el.value)); }); </script> </body> </html>

image577×115 8.02 KB

至于怎么发布就是告诉它需要用edgeone发布这个静态页即可,当然你也可以双击这个h5页面在本地使用。

特性列表:

# 特性 分类 说明
1 数据本地化 隐私 所有数据仅存储在浏览器本地(IndexedDB + localStorage),不经过任何第三方服务器。API Key、生成记录、图片数据全部留在用户设备上
2 P2P 直连 API 安全 浏览器直接连接用户配置的 API 端点,无需中间代理。密钥和请求内容不经过页面服务器中转
3 历史持久化 存储 基于 IndexedDB 存储所有生成记录,刷新页面、关闭浏览器后重新打开仍保留全部图片和提示词
4 缩略图画廊 浏览 按时间倒序展示所有历史图片的缩略图网格,最新生成排在最前,一键回溯所有作品
5 全屏大图预览 查看 点击任意图片即可全屏查看高清原图,支持 ESC 键快速关闭
6 多尺寸支持 灵活 内置 1024×1024 / 1024×1536 / 1536×1024 / Auto 四种尺寸预设
7 配置记忆 便捷 自动保存 Base URL、Model、API Key 到 localStorage,下次打开无需重新填写
8 提示词复用 效率 一键复制历史图片的 prompt 提示词,快速填入输入框进行微调或重新生成
9 独立下载 导出 每张图片支持单独下载为 PNG 文件,文件名自动包含日期和序号
10 灵活管理 控制 支持单条删除和一键清空全部历史,删除前有二次确认防止误操作
11 兼容 OpenAI 协议 通用 支持所有兼容 OpenAI Responses API 格式的服务商,填入 Base URL 即可切换
12 响应式布局 体验 自适应桌面和移动端屏幕,画廊网格自动调整列数

灵感来自于:周五使用codex 直接发提示词也能出图,当然站内也有人分享原理了。
好比: 没有gpt-image-2,也可以用gpt-5.2进行生图 - 开发调优 / 开发调优, Lv1 - LINUX DO

any 上的消耗图
image2100×298 32.5 KB
因为是非流 生图比较慢,耐心等待即可。

image1263×863 96.6 KB
更新了openai官网支持的参数
AI 图片生成器

网友解答:
--【壹】--:

佬,我发现不管图片尺寸选2K还是4K,生成出来的分辨率一直都是1672×941


--【贰】--:

好用,感谢!请问会考虑加入参考图片功能吗


--【叁】--:

浏览器cors的问题导致的,但是咋处理我也不会(,看看有没有大佬能解决


--【肆】--:

用any是不是不能指定2k,4k。我看传入参数也没用。还是auto和1k


--【伍】--:

能出2k4k或者0.5k这种吗,还是智能出1k的


--【陆】--:

为什么我使用别的第三方中转站都会提示这个,测试了好几个了
Screenshot2026-04-27-20-41-18-7216c8430dcd18ddacf572a6564ace01501240×2772 212 KB


--【柒】--:

感觉any的gpt,比claude便宜好多


--【捌】--:

为什么模型选择是5.3呢?
不太懂这个,可以在codex中用吗


--【玖】--:

右键选择查看网页源代码,或者ctrl+u。


--【拾】--:

原理就是提示词+工具调用触发底层调用gpt-image-2模型返图,为什么是5.3因为anyrouter里面只有5.3和5.0,支持 OpenAI Responses API 的5.X 模型应该都是可以的。


--【拾壹】--: 唐钰逍遥:

更新了openai官网支持的参数

经测试,完全可用,好用,赞
这个“更新了openai官网支持的参数” 的版本能提供HTML源码吗?谢谢


--【拾贰】--: 罗季安:

右键选择查看网页源代码,或者ctrl+u。

没注意看代码,贴文给出的已经是改过版的源码HTML了,我的疏忽


--【拾叁】--:

更了一版,支持了所有官网的参数。
AI 图片生成器


--【拾肆】--:

没有用any,用的其他第三方的,但是报错了佬
image1920×1066 114 KB


--【拾伍】--: 唐钰逍遥:

内置 1024×1024 / 1024×1536 / 1536×1024 / Auto 四种尺寸预设

为啥这个选了没啥用呢,是无法约束到么?。


--【拾陆】--:

佬,这个能输入参考图嘛,给他一张参考图然后让他按照参考图生图


--【拾柒】--:

嗯 gpt之前一直没人用,之前一直没法用于codex。


--【拾捌】--:

都行,参照源码,你改改你想要的参数就行。


--【拾玖】--:

真的好用
image1024×1536 599 KB
任意第三方API 公益站
image596×902 112 KB
楼主伟大

生成这张图片花费0.14元,离了谱的便宜…

问题描述:

能直接用的地址,只用填上你的key即可。
AI 图片生成器
效果图
image2348×1878 410 KB

下面是教程以及源码
在你的claude code 中安装如下的mcp
“mcpServers”: {
“edgeone-pages-mcp-server”: {
“url”: “https://mcp-on-edge.edgeone.app/mcp-server”
}
}
ps:使用cc desktop的需要重启desktop,才能使用部署好的edgeone mcp。
接下来就是你按照如下代码生成h5网页并使用mcp发布到edgeone,服务端会给你返回一个地址。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI 图片生成器</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); min-height: 100vh; color: #e0e0e0; } .container { max-width: 760px; margin: 0 auto; padding: 40px 20px; } h1 { text-align: center; font-size: 2em; margin-bottom: 8px; background: linear-gradient(90deg, #667eea, #764ba2, #f093fb); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .subtitle { text-align: center; color: #888; margin-bottom: 36px; font-size: 0.95em; } .card { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 28px; margin-bottom: 24px; backdrop-filter: blur(10px); } label { display: block; font-size: 0.85em; color: #aaa; margin-bottom: 6px; font-weight: 500; } input, textarea, select { width: 100%; padding: 12px 14px; border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; background: rgba(0,0,0,0.3); color: #f0f0f0; font-size: 0.95em; margin-bottom: 18px; transition: border-color 0.2s; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 80px; } .row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .btn-generate { width: 100%; padding: 14px; border: none; border-radius: 12px; font-size: 1.05em; font-weight: 600; cursor: pointer; background: linear-gradient(135deg, #667eea, #764ba2); color: white; transition: transform 0.15s, box-shadow 0.2s, opacity 0.2s; margin-top: 4px; } .btn-generate:hover { transform: translateY(-1px); box-shadow: 0 6px 24px rgba(102,126,234,0.4); } .btn-generate:active { transform: translateY(0); } .btn-generate:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none; } .result-area { text-align: center; min-height: 120px; display: flex; flex-direction: column; align-items: center; justify-content: center; } .result-area img { max-width: 100%; max-height: 520px; border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.4); cursor: pointer; transition: transform 0.2s; } .result-area img:hover { transform: scale(1.01); } .result-actions { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; justify-content: center; } .btn-action { padding: 10px 28px; border: 1px solid rgba(255,255,255,0.2); border-radius: 10px; background: rgba(255,255,255,0.08); color: #e0e0e0; cursor: pointer; font-size: 0.9em; transition: background 0.2s; } .btn-action:hover { background: rgba(255,255,255,0.15); } .spinner { width: 48px; height: 48px; border: 4px solid rgba(255,255,255,0.1); border-top-color: #667eea; border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .loading-text { margin-top: 16px; color: #aaa; font-size: 0.9em; } .placeholder-icon { font-size: 3em; margin-bottom: 12px; opacity: 0.3; } .placeholder-text { color: #555; font-size: 0.9em; } .error-msg { color: #ff6b6b; background: rgba(255,107,107,0.1); border: 1px solid rgba(255,107,107,0.2); border-radius: 10px; padding: 14px; margin-top: 12px; font-size: 0.9em; text-align: left; word-break: break-all; } .size-selector { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; } .size-option { padding: 8px 16px; border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; background: rgba(0,0,0,0.2); color: #ccc; cursor: pointer; font-size: 0.85em; transition: all 0.2s; } .size-option:hover { border-color: #667eea; color: #fff; } .size-option.active { border-color: #667eea; background: rgba(102,126,234,0.2); color: #fff; } /* 历史记录区域 */ .history-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; } .history-header h2 { font-size: 1.1em; color: #aaa; } .history-count { font-size: 0.8em; color: #666; background: rgba(255,255,255,0.05); padding: 4px 12px; border-radius: 20px; } .btn-clear { padding: 6px 16px; border: 1px solid rgba(255,100,100,0.3); border-radius: 8px; background: rgba(255,100,100,0.08); color: #ff8888; cursor: pointer; font-size: 0.8em; transition: all 0.2s; } .btn-clear:hover { background: rgba(255,100,100,0.15); border-color: rgba(255,100,100,0.5); } .history-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; } .history-item { position: relative; border-radius: 12px; overflow: hidden; cursor: pointer; border: 2px solid transparent; transition: all 0.2s; aspect-ratio: 1; background: rgba(0,0,0,0.3); } .history-item:hover { border-color: #667eea; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.4); } .history-item.active { border-color: #f093fb; box-shadow: 0 0 0 2px rgba(240,147,251,0.3); } .history-item img { width: 100%; height: 100%; object-fit: cover; } .history-item .item-meta { position: absolute; bottom: 0; left: 0; right: 0; padding: 6px 8px; background: linear-gradient(transparent, rgba(0,0,0,0.8)); font-size: 0.7em; color: #ccc; display: flex; justify-content: space-between; align-items: center; } .history-item .item-index { position: absolute; top: 6px; left: 6px; background: rgba(0,0,0,0.6); color: #aaa; font-size: 0.7em; padding: 2px 8px; border-radius: 10px; } .history-item .btn-del { background: none; border: none; color: #ff6b6b; cursor: pointer; font-size: 1em; padding: 0 4px; opacity: 0; transition: opacity 0.2s; } .history-item:hover .btn-del { opacity: 1; } .history-empty { text-align: center; padding: 40px 20px; color: #555; } /* 全屏预览 */ .lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.92); z-index: 1000; justify-content: center; align-items: center; cursor: zoom-out; } .lightbox.show { display: flex; } .lightbox img { max-width: 95vw; max-height: 92vh; border-radius: 8px; box-shadow: 0 12px 48px rgba(0,0,0,0.6); } .lightbox-close { position: fixed; top: 20px; right: 24px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: white; width: 40px; height: 40px; border-radius: 50%; font-size: 1.2em; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.2s; } .lightbox-close:hover { background: rgba(255,255,255,0.2); } footer { text-align: center; color: #444; font-size: 0.8em; margin-top: 40px; } footer a { color: #667eea; text-decoration: none; } </style> </head> <body> <div class="container"> <h1>🎨 AI 图片生成器</h1> <p class="subtitle">基于 OpenAI Responses API,在浏览器中直接生成图片</p> <div class="card"> <div class="row"> <div> <label>Base URL</label> <input type="text" id="baseUrl" placeholder="https://api.openai.com/v1" value="https://anyrouter.top/v1"> </div> <div> <label>Model</label> <input type="text" id="model" placeholder="gpt-5.4" value="gpt-5.3-codex"> </div> </div> <label>API Key</label> <input type="password" id="apiKey" placeholder="sk-...(密钥仅在浏览器本地使用,不会上传)"> <label>图片尺寸</label> <div class="size-selector" id="sizeSelector"> <div class="size-option" data-size="1024x1024">1024×1024</div> <div class="size-option active" data-size="1024x1536">1024×1536</div> <div class="size-option" data-size="1536x1024">1536×1024</div> <div class="size-option" data-size="auto">Auto</div> </div> <label>提示词</label> <textarea id="prompt" placeholder="描述你想生成的图片...">为我生成图中角色的绘制 Q 版的,LINE 风格的半身像表情包,注意头饰要正确 彩色手绘风格,使用 4x6 布局,涵盖各种各样的常用聊天语句,或是一些有关的娱乐 meme 其他需求:不要原图复制。所有标注为手写简体中文。 生成的图片需为 2K 分辨率 16:9</textarea> <button class="btn-generate" id="btnGenerate" onclick="generate()">✨ 生成图片</button> </div> <!-- 当前结果 --> <div class="card"> <div class="result-area" id="resultArea"> <div class="placeholder-icon">🖼️</div> <div class="placeholder-text">图片将显示在这里</div> </div> </div> <!-- 历史记录 --> <div class="card" id="historyCard"> <div class="history-header"> <h2>📚 历史记录 <span class="history-count" id="historyCount">0 张</span></h2> <button class="btn-clear" id="btnClear" onclick="clearHistory()">🗑️ 清空</button> </div> <div id="historyGrid" class="history-grid"> <div class="history-empty">还没有生成过图片</div> </div> </div> <footer> Powered by <a href="https://edgeone.ai" target="_blank">EdgeOne Pages</a> · API 调用由浏览器直连,密钥不会经过第三方 · 历史图片存储在浏览器 IndexedDB 中 </footer> </div> <!-- 全屏预览 --> <div class="lightbox" id="lightbox" onclick="closeLightbox()"> <button class="lightbox-close" onclick="closeLightbox()">✕</button> <img id="lightboxImg" src="" alt="Preview" /> </div> <script> let selectedSize = '1024x1536'; let history = []; let activeIndex = -1; const DB_NAME = 'AIImageGenerator'; const DB_VERSION = 1; const STORE_NAME = 'images'; // ── IndexedDB ── function openDB() { return new Promise((resolve, reject) => { const req = indexedDB.open(DB_NAME, DB_VERSION); req.onupgradeneeded = (e) => { const db = e.target.result; if (!db.objectStoreNames.contains(STORE_NAME)) { db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true }); } }; req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } async function saveToDB(item) { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); const req = store.add(item); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } async function loadAllFromDB() { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readonly'); const store = tx.objectStore(STORE_NAME); const req = store.getAll(); req.onsuccess = () => resolve(req.result); req.onerror = () => reject(req.error); }); } async function deleteFromDB(id) { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); const req = store.delete(id); req.onsuccess = () => resolve(); req.onerror = () => reject(req.error); }); } async function clearDB() { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); const req = store.clear(); req.onsuccess = () => resolve(); req.onerror = () => reject(req.error); }); } // ── 初始化 ── document.querySelectorAll('.size-option').forEach(el => { el.addEventListener('click', () => { document.querySelectorAll('.size-option').forEach(e => e.classList.remove('active')); el.classList.add('active'); selectedSize = el.dataset.size; }); }); async function init() { try { history = await loadAllFromDB(); renderHistory(); if (history.length > 0) { showImage(history.length - 1); } } catch (e) { console.warn('IndexedDB load failed:', e); } } init(); // ── 渲染历史 ── function renderHistory() { const grid = document.getElementById('historyGrid'); const count = document.getElementById('historyCount'); count.textContent = history.length + ' 张'; if (history.length === 0) { grid.innerHTML = '<div class="history-empty">还没有生成过图片</div>'; return; } // 按时间倒序显示(最新在前) const sorted = [...history].reverse(); grid.innerHTML = sorted.map((item, ri) => { const realIndex = history.length - 1 - ri; const time = new Date(item.timestamp).toLocaleString('zh-CN', { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }); const shortPrompt = item.prompt.length > 20 ? item.prompt.slice(0, 20) + '…' : item.prompt; return ` <div class="history-item ${realIndex === activeIndex ? 'active' : ''}" onclick="showImage(${realIndex})"> <span class="item-index">#${realIndex + 1}</span> <img src="data:image/png;base64,${item.base64}" alt="${shortPrompt}" loading="lazy" /> <div class="item-meta"> <span>${time}</span> <button class="btn-del" onclick="event.stopPropagation();deleteImage(${realIndex})" title="删除">✕</button> </div> </div> `; }).join(''); } function showImage(index) { if (index < 0 || index >= history.length) return; activeIndex = index; const item = history[index]; const area = document.getElementById('resultArea'); const shortPrompt = item.prompt.length > 60 ? item.prompt.slice(0, 60) + '…' : item.prompt; area.innerHTML = ` <img src="data:image/png;base64,${item.base64}" alt="Generated Image" onclick="openLightbox(${index})" title="点击查看大图" /> <div style="margin-top:10px;color:#888;font-size:0.8em;max-width:500px;text-align:center">${shortPrompt}</div> <div class="result-actions"> <button class="btn-action" onclick="downloadImage(${index})">💾 下载</button> <button class="btn-action" onclick="copyPrompt(${index})">📋 复制提示词</button> <button class="btn-action" onclick="openLightbox(${index})">🔍 大图</button> </div> `; renderHistory(); } // ── 生成 ── function setGenerating(on) { const btn = document.getElementById('btnGenerate'); const area = document.getElementById('resultArea'); btn.disabled = on; if (on) { area.innerHTML = '<div class="spinner"></div><div class="loading-text">正在生成图片,请稍候...</div>'; } } function showError(msg) { const area = document.getElementById('resultArea'); area.innerHTML = `<div class="placeholder-icon">⚠️</div><div class="error-msg">${msg}</div>`; } async function generate() { const apiKey = document.getElementById('apiKey').value.trim(); const baseUrl = document.getElementById('baseUrl').value.trim(); const model = document.getElementById('model').value.trim(); const prompt = document.getElementById('prompt').value.trim(); if (!apiKey) { showError('请输入 API Key'); return; } if (!baseUrl) { showError('请输入 Base URL'); return; } if (!prompt) { showError('请输入提示词'); return; } setGenerating(true); try { const url = baseUrl.replace(/\/+$/, '') + '/responses'; const body = { model: model || 'gpt-5.4', input: prompt, tools: [{ type: 'image_generation' }] }; if (selectedSize !== 'auto') { body.tools[0].size = selectedSize; } const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify(body) }); if (!resp.ok) { const errText = await resp.text(); let errMsg = `HTTP ${resp.status}`; try { errMsg += ': ' + JSON.parse(errText).error?.message || errText; } catch { errMsg += ': ' + errText.slice(0, 300); } throw new Error(errMsg); } const data = await resp.json(); const imageOutputs = (data.output || []).filter(o => o.type === 'image_generation_call'); if (!imageOutputs.length) { throw new Error('API 返回成功但未包含生成的图片。响应: ' + JSON.stringify(data).slice(0, 500)); } const base64 = imageOutputs[0].result; const item = { base64, prompt, model: model || 'gpt-5.4', size: selectedSize, timestamp: Date.now() }; // 存入 IndexedDB try { const id = await saveToDB(item); item.id = id; } catch (e) { console.warn('IndexedDB save failed, using memory only:', e); } history.push(item); showImage(history.length - 1); } catch (err) { showError(err.message); } finally { document.getElementById('btnGenerate').disabled = false; } } // ── 操作 ── function downloadImage(index) { if (index < 0 || index >= history.length) return; const item = history[index]; const a = document.createElement('a'); a.href = 'data:image/png;base64,' + item.base64; a.download = 'ai-image-' + new Date(item.timestamp).toISOString().slice(0,10) + '-' + (index + 1) + '.png'; a.click(); } function copyPrompt(index) { if (index < 0 || index >= history.length) return; navigator.clipboard.writeText(history[index].prompt).then(() => { const btn = event.target; const orig = btn.textContent; btn.textContent = '✅ 已复制'; setTimeout(() => btn.textContent = orig, 1500); }); } async function deleteImage(index) { if (index < 0 || index >= history.length) return; const item = history[index]; try { await deleteFromDB(item.id); } catch (e) { console.warn(e); } history.splice(index, 1); if (activeIndex === index) { activeIndex = history.length > 0 ? Math.min(index, history.length - 1) : -1; if (activeIndex >= 0) showImage(activeIndex); else document.getElementById('resultArea').innerHTML = '<div class="placeholder-icon">🖼️</div><div class="placeholder-text">图片将显示在这里</div>'; } else if (activeIndex > index) { activeIndex--; } renderHistory(); } async function clearHistory() { if (!confirm('确定清空所有历史记录?此操作不可撤销。')) return; try { await clearDB(); } catch (e) { console.warn(e); } history = []; activeIndex = -1; document.getElementById('resultArea').innerHTML = '<div class="placeholder-icon">🖼️</div><div class="placeholder-text">图片将显示在这里</div>'; renderHistory(); } // ── 全屏预览 ── function openLightbox(index) { if (index < 0 || index >= history.length) return; const img = document.getElementById('lightboxImg'); img.src = 'data:image/png;base64,' + history[index].base64; document.getElementById('lightbox').classList.add('show'); document.body.style.overflow = 'hidden'; } function closeLightbox() { document.getElementById('lightbox').classList.remove('show'); document.body.style.overflow = ''; } document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeLightbox(); }); // ── 保存/恢复设置 ── ['apiKey', 'baseUrl', 'model'].forEach(id => { const el = document.getElementById(id); const saved = localStorage.getItem('imggen_' + id); if (saved) el.value = saved; el.addEventListener('change', () => localStorage.setItem('imggen_' + id, el.value)); }); </script> </body> </html>

image577×115 8.02 KB

至于怎么发布就是告诉它需要用edgeone发布这个静态页即可,当然你也可以双击这个h5页面在本地使用。

特性列表:

# 特性 分类 说明
1 数据本地化 隐私 所有数据仅存储在浏览器本地(IndexedDB + localStorage),不经过任何第三方服务器。API Key、生成记录、图片数据全部留在用户设备上
2 P2P 直连 API 安全 浏览器直接连接用户配置的 API 端点,无需中间代理。密钥和请求内容不经过页面服务器中转
3 历史持久化 存储 基于 IndexedDB 存储所有生成记录,刷新页面、关闭浏览器后重新打开仍保留全部图片和提示词
4 缩略图画廊 浏览 按时间倒序展示所有历史图片的缩略图网格,最新生成排在最前,一键回溯所有作品
5 全屏大图预览 查看 点击任意图片即可全屏查看高清原图,支持 ESC 键快速关闭
6 多尺寸支持 灵活 内置 1024×1024 / 1024×1536 / 1536×1024 / Auto 四种尺寸预设
7 配置记忆 便捷 自动保存 Base URL、Model、API Key 到 localStorage,下次打开无需重新填写
8 提示词复用 效率 一键复制历史图片的 prompt 提示词,快速填入输入框进行微调或重新生成
9 独立下载 导出 每张图片支持单独下载为 PNG 文件,文件名自动包含日期和序号
10 灵活管理 控制 支持单条删除和一键清空全部历史,删除前有二次确认防止误操作
11 兼容 OpenAI 协议 通用 支持所有兼容 OpenAI Responses API 格式的服务商,填入 Base URL 即可切换
12 响应式布局 体验 自适应桌面和移动端屏幕,画廊网格自动调整列数

灵感来自于:周五使用codex 直接发提示词也能出图,当然站内也有人分享原理了。
好比: 没有gpt-image-2,也可以用gpt-5.2进行生图 - 开发调优 / 开发调优, Lv1 - LINUX DO

any 上的消耗图
image2100×298 32.5 KB
因为是非流 生图比较慢,耐心等待即可。

image1263×863 96.6 KB
更新了openai官网支持的参数
AI 图片生成器

网友解答:
--【壹】--:

佬,我发现不管图片尺寸选2K还是4K,生成出来的分辨率一直都是1672×941


--【贰】--:

好用,感谢!请问会考虑加入参考图片功能吗


--【叁】--:

浏览器cors的问题导致的,但是咋处理我也不会(,看看有没有大佬能解决


--【肆】--:

用any是不是不能指定2k,4k。我看传入参数也没用。还是auto和1k


--【伍】--:

能出2k4k或者0.5k这种吗,还是智能出1k的


--【陆】--:

为什么我使用别的第三方中转站都会提示这个,测试了好几个了
Screenshot2026-04-27-20-41-18-7216c8430dcd18ddacf572a6564ace01501240×2772 212 KB


--【柒】--:

感觉any的gpt,比claude便宜好多


--【捌】--:

为什么模型选择是5.3呢?
不太懂这个,可以在codex中用吗


--【玖】--:

右键选择查看网页源代码,或者ctrl+u。


--【拾】--:

原理就是提示词+工具调用触发底层调用gpt-image-2模型返图,为什么是5.3因为anyrouter里面只有5.3和5.0,支持 OpenAI Responses API 的5.X 模型应该都是可以的。


--【拾壹】--: 唐钰逍遥:

更新了openai官网支持的参数

经测试,完全可用,好用,赞
这个“更新了openai官网支持的参数” 的版本能提供HTML源码吗?谢谢


--【拾贰】--: 罗季安:

右键选择查看网页源代码,或者ctrl+u。

没注意看代码,贴文给出的已经是改过版的源码HTML了,我的疏忽


--【拾叁】--:

更了一版,支持了所有官网的参数。
AI 图片生成器


--【拾肆】--:

没有用any,用的其他第三方的,但是报错了佬
image1920×1066 114 KB


--【拾伍】--: 唐钰逍遥:

内置 1024×1024 / 1024×1536 / 1536×1024 / Auto 四种尺寸预设

为啥这个选了没啥用呢,是无法约束到么?。


--【拾陆】--:

佬,这个能输入参考图嘛,给他一张参考图然后让他按照参考图生图


--【拾柒】--:

嗯 gpt之前一直没人用,之前一直没法用于codex。


--【拾捌】--:

都行,参照源码,你改改你想要的参数就行。


--【拾玖】--:

真的好用
image1024×1536 599 KB
任意第三方API 公益站
image596×902 112 KB
楼主伟大

生成这张图片花费0.14元,离了谱的便宜…