diff --git a/common/js/media-upload.js b/common/js/media-upload.js index 9f5063c..1649855 100644 --- a/common/js/media-upload.js +++ b/common/js/media-upload.js @@ -227,13 +227,19 @@ async function uploadViaPhp(filePath, kind, addon, agentId, knownSize = 0) { * { provider, bucket, region, accessKeyId, accessKeySecret, securityToken, * key_prefix_tpl, site_id, host? (single-URL 模式), endpoint? (qiniu 7 参兼容) } */ -async function uploadViaSts(filePath, kind, addon, agentId, config, knownSize = 0) { - // PR-FRONTEND-MEDIA-UPLOAD-FIX (2026-09-16): 用更稳的扩展名提取 - // H5 blob URL (blob:https://xxx/uuid) 无扩展名 → split('.').pop() 会拿整条 URL - // 优先 file.name (H5 file / APP tempFile.name), 都没有就 blob URL → 'bin' +/** + * PR-FRONTEND-MEDIA-UPLOAD-FIX (2026-09-16): 三段兜底扩展名提取 + * 1) filePath 最后一段正则匹配 .xxx + * 2) filePath.name 兜底 (H5 File 对象 / APP tempFile.name) + * 3) mimeHint 反查 EXT_MIME_MAP (H5 blob URL 没扩展名但 blob.type 通常是 image/png) + * 4) 最终 'bin' (防 URL 片段被误当扩展名) + * + * PR-FRONTEND-MEDIA-UPLOAD-FIX-HOTFIX (2026-09-16): 提到模块顶层, + * 之前只在 uploadViaSts 里, pathToFile 引用不到 → "extractExt is not defined" + */ +function extractExt(filePath, mimeHint = '') { let ext = 'bin' if (typeof filePath === 'string') { - // 1) 优先从 filePath 里找 .xxx (路径最后一段) const lastSeg = filePath.split('?')[0].split('/').pop() || '' const m = lastSeg.match(/\.([a-z0-9]+)$/i) if (m) ext = m[1].toLowerCase() @@ -242,7 +248,22 @@ async function uploadViaSts(filePath, kind, addon, agentId, config, knownSize = const m2 = String(filePath.name).match(/\.([a-z0-9]+)$/i) if (m2) ext = m2[1].toLowerCase() } - if (!/^[a-z0-9]{2,5}$/.test(ext)) ext = 'bin' // 防止 ext 是 'https' 这种 URL 片段 + // 3) mime→ext 反查 (PR-FRONTEND-MEDIA-UPLOAD-FIX-HOTFIX): blob URL 无扩展名但 + // blob.type 是 image/png 这种, 反查 EXT_MIME_MAP → png + if (ext === 'bin' && mimeHint) { + for (const [e, m] of Object.entries(EXT_MIME_MAP)) { + if (m === mimeHint) { ext = e; break } + } + } + if (!/^[a-z0-9]{2,5}$/.test(ext)) ext = 'bin' + return ext +} + +async function uploadViaSts(filePath, kind, addon, agentId, config, knownSize = 0) { + // PR-FRONTEND-MEDIA-UPLOAD-FIX (2026-09-16): 用更稳的扩展名提取 + // H5 blob URL (blob:https://xxx/uuid) 无扩展名 → split('.').pop() 会拿整条 URL + // 优先 file.name (H5 file / APP tempFile.name), 都没有就 blob URL → 'bin' + const ext = extractExt(filePath) // 1. 拿 STS 凭证 (addon 模式 URL) const stsUrl = (addon === 'aikefu' ? '/aikefu/api/upload/' : '/aiagent/api/upload/') + 'ossSts' @@ -393,7 +414,17 @@ async function pathToFile(filePath, kind) { // #ifdef H5 const res = await fetch(filePath) const blob = await res.blob() - return new File([blob], filePath.split('/').pop() || `${kind}.bin`, { type: blob.type }) + // PR-FRONTEND-MEDIA-UPLOAD-FIX-HOTFIX (2026-09-16): H5 blob URL 路径修复扩展名 + // 原代码 `filePath.split('/').pop()` 对 blob:http://xxx/uuid 取出 'uuid' (无扩展名) + // → 后端 multipart filename 缺失扩展名 → ext_rule 拒绝 "上传文件后缀不允许" + // extractExt 三段兜底: 1) lastSegment 正则 2) file.name 3) 'bin' 最终兜底 + // 若 ext='bin', 走 guessMime(EXT_MIME_MAP) 看能不能从 mime 反推 jpg/png (mime→ext) + const rawName = filePath.split('/').pop() || `${kind}.bin` + const ext = extractExt(filePath, blob.type) + const finalName = ext !== 'bin' ? `${rawName}.${ext}` : rawName + // blob.type 通常是 image/png 或空; 空时用 guessMime 兜底 + const mime = blob.type || guessMime(ext) + return new File([blob], finalName, { type: mime }) // #endif }