Files
hair/static/img_downscale.js
T
xsl 544c2a83d6 feat(gateway): 接入52号发际线接口与公开测试页
新增52号同路径转发,沿用现有主备派发、鉴权、队列和图片URL改写。空图片保留既有base64字段名,不带入worker算法及IP限制。

同步52号测试页、图片解析和矩形叠加依赖,公开页面不包含内部Token。补全网关契约与部署文档,Nginx示例读取超时延长至660秒。

验证:42项网关测试通过;临时网关连接真实GPU worker,5号三档与52号七型URL、长图裁切和边框均通过浏览器检查。
2026-11-04 11:15:58 +08:00

86 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 上传图片自动降采样:当总像素 > MAX_PIXELS 时,等比例缩小到 <= TARGET_PIXELS。
// 用法:在提交前 file = await window.downscaleImageFile(file);
(function () {
var MAX_PIXELS = 1536000; // 触发阈值:超过则缩小
var TARGET_PIXELS = 786432; // 缩小后总像素上限
function loadImage(url) {
return new Promise(function (resolve, reject) {
var img = new Image();
img.onload = function () { resolve(img); };
img.onerror = reject;
img.src = url;
});
}
async function downscaleImageFile(file) {
// 非图片、GIF、SVG 不处理,原样返回
if (!file || !file.type || file.type.indexOf('image/') !== 0) return file;
if (file.type === 'image/gif' || file.type === 'image/svg+xml') return file;
var url = URL.createObjectURL(file);
try {
var img = await loadImage(url);
var w = img.naturalWidth, h = img.naturalHeight;
var total = w * h;
if (!total || total <= MAX_PIXELS) return file; // 未超阈值,原图直接用
var scale = Math.sqrt(TARGET_PIXELS / total);
var nw = Math.max(1, Math.round(w * scale));
var nh = Math.max(1, Math.round(h * scale));
var canvas = document.createElement('canvas');
canvas.width = nw;
canvas.height = nh;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, nw, nh);
var outType = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
var quality = outType === 'image/jpeg' ? 0.92 : undefined;
var blob = await new Promise(function (res) { canvas.toBlob(res, outType, quality); });
if (!blob) return file;
var base = (file.name || 'image').replace(/\.(jpe?g|png|webp|bmp)$/i, '');
var name = base + (outType === 'image/png' ? '.png' : '.jpg');
return new File([blob], name, { type: outType, lastModified: Date.now() });
} catch (e) {
console.warn('downscaleImageFile 失败,使用原图', e);
return file;
} finally {
URL.revokeObjectURL(url);
}
}
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;
})();