fix(media-upload): H5 pathToFile 修复 multipart filename 缺扩展名
PR-FRONTEND-MEDIA-UPLOAD-FIX-HOTFIX (2026-09-16)
诊断: 直接打后端 (curl) 验证后端 ext_rule 完全 OK,
所有 jpg/png/heic 标准 mime 都返回 code:0, 只有 filename=空
(无扩展名) 才会被拒. 所以问题在前端.
定位: common/js/media-upload.js pathToFile() H5 分支:
原代码 filePath.split('/').pop() 对 blob:http://xxx/uuid
取出 'uuid' (无扩展名)
→ new File([blob], 'uuid', ...) 没有 .jpg/.png 后缀
→ multipart Content-Disposition: filename='uuid'
→ 后端 fileExt 规则检查 'uuid' 不在白名单 → 拒绝
修复: 提取 extractExt() 到模块顶层 (之前内联在 uploadViaSts 里),
pathToFile 调用 extractExt(filePath, blob.type) 三段兜底:
1) filePath 最后一段正则
2) filePath.name 兜底
3) blob.type mimeHint 反查 EXT_MIME_MAP → png/jpg/heic 等
修复后 multipart filename = 'test-img1.png' (有扩展名),
后端 ext_rule 通过, 上传成功.
E2E 验证 (verify_filename2.py):
- 修复前: filename='test-img2' → 后端拒绝
- 修复后: filename='test-img2.png' → 后端接受
教训: PR-FRONTEND-MEDIA-UPLOAD-FIX 加 extractExt 时只修了 STS 路径,
PHP 路径漏修; H5 blob URL + multipart 是双盲点 (前端不知道后端需要
filename 扩展名, 后端拿到 filename 才检查). 任何 file.toBlob() 或
URL.createObjectURL 出来的 file 都要在 File 构造时补扩展名.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user