// 上传图片自动降采样:当总像素 > 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; /** * 将接口返回的图片字段规范成 可用值。 * 同时兼容: * - 网关改写后的 *_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; })();