Files
hair/static/img_downscale.js
T
xslandCursor 08b31a3baa fix: 测试页同时兼容 base64 与 URL 图片字段
直连 worker 返回 *_base64、经网关则改写为 *_url;统一 resolveImgSrc 后接口1/2/3/5/6 测试页都能正确显示结果图。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-19 15:49:46 +08:00

87 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> 可用值。
* 同时兼容:
* - 网关改写后的 *_urlhttp/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;
})();