fix: 测试页同时兼容 base64 与 URL 图片字段
直连 worker 返回 *_base64、经网关则改写为 *_url;统一 resolveImgSrc 后接口1/2/3/5/6 测试页都能正确显示结果图。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -52,4 +52,35 @@
|
||||
}
|
||||
|
||||
window.downscaleImageFile = downscaleImageFile;
|
||||
|
||||
/**
|
||||
* 将接口返回的图片字段规范成 <img src> 可用值。
|
||||
* 同时兼容:
|
||||
* - 网关改写后的 *_url(http/https/相对路径)
|
||||
* - worker 直连的 *_base64(原始 base64,或已带 data:image/...;base64, 前缀)
|
||||
* @param {string|null|undefined} url
|
||||
* @param {string|null|undefined} b64
|
||||
* @param {string} [mimeHint='image/jpeg'] 原始 base64 时的 MIME 提示(会按魔数再嗅探)
|
||||
* @returns {string}
|
||||
*/
|
||||
function resolveImgSrc(url, b64, mimeHint) {
|
||||
var v = url || b64 || '';
|
||||
if (!v || typeof v !== 'string') return '';
|
||||
v = v.trim();
|
||||
if (!v) return '';
|
||||
// 已是可用 URL / data URI / blob / 协议相对 URL
|
||||
if (/^(https?:|blob:|data:|\/\/)/i.test(v)) return v;
|
||||
// 同站相对路径(仅匹配明确的 /static/...;勿把 JPEG base64 的 /9j/ 前缀当路径)
|
||||
if (/^\/static\//.test(v)) return v;
|
||||
// 原始 base64:去掉可能残留的 data URI 前缀后再拼,避免双重前缀导致无法显示
|
||||
var raw = v.replace(/^data:image\/[a-zA-Z0-9+.-]+;base64,/i, '');
|
||||
if (!raw) return '';
|
||||
var mime = mimeHint || 'image/jpeg';
|
||||
if (raw.indexOf('iVBOR') === 0) mime = 'image/png';
|
||||
else if (raw.indexOf('/9j/') === 0) mime = 'image/jpeg';
|
||||
else if (raw.indexOf('R0lGOD') === 0) mime = 'image/gif';
|
||||
else if (raw.indexOf('UklGR') === 0) mime = 'image/webp';
|
||||
return 'data:' + mime + ';base64,' + raw;
|
||||
}
|
||||
window.resolveImgSrc = resolveImgSrc;
|
||||
})();
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
@media (max-width: 768px) { .results { flex-direction: column; } }
|
||||
.hidden { display: none !important; }
|
||||
</style>
|
||||
<script src="/static/img_downscale.js"></script>
|
||||
<script src="/static/img_downscale.js?v=2"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -159,12 +159,9 @@ async function submitTest() {
|
||||
|
||||
if (json.code === 0) {
|
||||
setStatus('✅ 请求成功 (' + _elapsed + 's) — request_id: ' + json.request_id, 'success');
|
||||
// 兼容本地直连 worker(返回 *_base64)与网关(改写为 *_url)两种情况
|
||||
// 兼容本地直连 worker(*_base64)与网关(*_url)
|
||||
const _d = json.data || {};
|
||||
const annoUrl = _d.annotated_image_url
|
||||
|| (_d.annotated_image_base64
|
||||
? ('data:image/png;base64,' + _d.annotated_image_base64.replace(/^data:image\/\w+;base64,/, ''))
|
||||
: '');
|
||||
const annoUrl = resolveImgSrc(_d.annotated_image_url, _d.annotated_image_base64, 'image/png');
|
||||
showOverlay(annoUrl);
|
||||
renderMetrics(json.data);
|
||||
$('metricsBar').classList.remove('hidden');
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
.hidden { display: none !important; }
|
||||
</style>
|
||||
<script src="/static/img_downscale.js"></script>
|
||||
<script src="/static/img_downscale.js?v=2"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -180,9 +180,9 @@ function renderSchemes(results) {
|
||||
let html = '';
|
||||
results.forEach((r, i) => {
|
||||
const lb = TYPE_LABELS[r.hairline_type] || r.hairline_type;
|
||||
// 直连 worker 时后端返回 base64(网关才会改写为 *_url),URL 缺失则回退 data URI
|
||||
const overlaySrc = r.image_url || (r.image_base64 ? 'data:image/png;base64,' + r.image_base64 : '');
|
||||
const grownSrc = r.grown_image_url || (r.grown_image_base64 ? 'data:image/jpeg;base64,' + r.grown_image_base64 : '');
|
||||
// 兼容网关 *_url 与 worker *_base64(原始或 data URI)
|
||||
const overlaySrc = resolveImgSrc(r.image_url, r.image_base64, 'image/png');
|
||||
const grownSrc = resolveImgSrc(r.grown_image_url, r.grown_image_base64, 'image/jpeg');
|
||||
|
||||
// 原图
|
||||
const origSlot = '<div class="img-slot">'+
|
||||
@@ -206,7 +206,7 @@ function renderSchemes(results) {
|
||||
} else {
|
||||
grownSlot = '<div class="img-slot">'+
|
||||
'<div class="label"><span class="dot grown"></span>生发效果</div>'+
|
||||
'<div class="na">⚠ 未返回<br><span style="font-size:10px;color:#9ca3af">ComfyUI 未就绪或生成失败</span></div></div>';
|
||||
'<div class="na">⚠ 未返回<br><span style="font-size:10px;color:#9ca3af">生发失败(female=换发型/重绘,male=ComfyUI)</span></div></div>';
|
||||
}
|
||||
|
||||
html += '<div class="scheme-card">'+
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
@media (max-width: 800px) { .results-layout, .preview-row { flex-direction: column; } }
|
||||
</style>
|
||||
<script src="/static/img_downscale.js"></script>
|
||||
<script src="/static/img_downscale.js?v=2"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -175,7 +175,7 @@ async function submitTest() {
|
||||
$('hairlineType').style.display = 'inline-block';
|
||||
}
|
||||
|
||||
const grownSrc = d.hair_growth_image_url || (d.hair_growth_image_base64 ? 'data:image/jpeg;base64,' + d.hair_growth_image_base64 : '');
|
||||
const grownSrc = resolveImgSrc(d.hair_growth_image_url, d.hair_growth_image_base64, 'image/jpeg');
|
||||
if (grownSrc) {
|
||||
$('blendTop').src = grownSrc;
|
||||
$('blendTop').style.display = 'block';
|
||||
|
||||
+16
-11
@@ -88,7 +88,7 @@
|
||||
.hidden { display: none !important; }
|
||||
@media (max-width: 800px) { .results-layout { flex-direction: column; } }
|
||||
</style>
|
||||
<script src="/static/img_downscale.js"></script>
|
||||
<script src="/static/img_downscale.js?v=2"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -237,25 +237,30 @@ async function submitTest() {
|
||||
function renderGrid() {
|
||||
if (!_images.length) { $('resultsGrid').innerHTML = '<span style="color:#9ca3af">无数据</span>'; return; }
|
||||
let h = '';
|
||||
// 叠图档:原图打底 + 透明 PNG 叠加(无原图或无叠图 → 显示占位)
|
||||
const overlayCell = (label, url) => (url && _origUrl)
|
||||
// 叠图档:原图打底 + 透明 PNG 图层(无原图或无叠图 → 显示占位)
|
||||
const overlayCell = (label, src) => (src && _origUrl)
|
||||
? '<div class="level-cell"><div class="level-label">'+label+'</div><div class="img-stack">'+
|
||||
'<img class="layer-base" src="'+_origUrl+'" alt="原图">'+
|
||||
'<img class="layer-anno" src="'+url+'" alt="'+label+'"></div></div>'
|
||||
'<img class="layer-anno" src="'+src+'" alt="'+label+'"></div></div>'
|
||||
: '<div class="level-cell"><div class="level-label">'+label+'</div><div class="level-none">无</div></div>';
|
||||
// 生发图:独立 img(已是 ComfyUI 完整人像照片)
|
||||
const grownCell = (label, url) => url
|
||||
? '<div class="level-cell"><div class="level-label">'+label+'</div><img src="'+url+'" alt="'+label+'"></div>'
|
||||
const grownCell = (label, src) => src
|
||||
? '<div class="level-cell"><div class="level-label">'+label+'</div><img src="'+src+'" alt="'+label+'"></div>'
|
||||
: '<div class="level-cell"><div class="level-label">'+label+'</div><div class="level-none">无</div></div>';
|
||||
_images.forEach(function(it) {
|
||||
// 兼容网关 *_url 与 worker *_base64
|
||||
const mid = resolveImgSrc(it.image_middle_url, it.image_middle_base64, 'image/png');
|
||||
const high = resolveImgSrc(it.image_high_url, it.image_high_base64, 'image/png');
|
||||
const low = resolveImgSrc(it.image_low_url, it.image_low_base64, 'image/png');
|
||||
const grown = resolveImgSrc(it.grown_image_url, it.grown_image_base64, 'image/jpeg');
|
||||
h += '<div class="hair-block">' +
|
||||
'<div class="hair-block-title">#' + (it.order||'—') + ' ' + (it.hairline_type||'') +
|
||||
(it.grown_image_url ? ' <span style="font-size:11px;color:#7c3aed">含生发图</span>' : '') + '</div>' +
|
||||
(grown ? ' <span style="font-size:11px;color:#7c3aed">含生发图</span>' : '') + '</div>' +
|
||||
'<div class="level-row">' +
|
||||
overlayCell('middle', it.image_middle_url) +
|
||||
overlayCell('high', it.image_high_url) +
|
||||
overlayCell('low', it.image_low_url) +
|
||||
grownCell('生发图', it.grown_image_url) +
|
||||
overlayCell('middle', mid) +
|
||||
overlayCell('high', high) +
|
||||
overlayCell('low', low) +
|
||||
grownCell('生发图', grown) +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
.diff-list { font-size: 12px; color: #6b7280; margin-top: 6px; line-height: 1.7; }
|
||||
.diff-list li { margin-left: 18px; }
|
||||
</style>
|
||||
<script src="/static/img_downscale.js"></script>
|
||||
<script src="/static/img_downscale.js?v=2"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -161,12 +161,9 @@ async function submitTest() {
|
||||
|
||||
if (json.code === 0) {
|
||||
setStatus('✅ 请求成功 (' + _elapsed + 's) — request_id: ' + json.request_id, 'success');
|
||||
// 兼容本地直连 worker(返回 *_base64)与网关(改写为 *_url)两种情况
|
||||
// 兼容本地直连 worker(*_base64)与网关(*_url)
|
||||
const _d = json.data || {};
|
||||
const annoUrl = _d.annotated_image_url
|
||||
|| (_d.annotated_image_base64
|
||||
? ('data:image/png;base64,' + _d.annotated_image_base64.replace(/^data:image\/\w+;base64,/, ''))
|
||||
: '');
|
||||
const annoUrl = resolveImgSrc(_d.annotated_image_url, _d.annotated_image_base64, 'image/png');
|
||||
showOverlay(annoUrl);
|
||||
renderMetrics(json.data);
|
||||
$('metricsBar').classList.remove('hidden');
|
||||
|
||||
Reference in New Issue
Block a user