import { getJavaApi } from '@/utils/java-api'
|
|
const imageExtension = /\.(jpg|jpeg|png|gif|bmp|webp|svg|ico|tiff?|avif|heic)$/i
|
|
function decodePart(value) {
|
try {
|
return decodeURIComponent(value)
|
} catch {
|
return value
|
}
|
}
|
|
// 数据库可能保存文件名、静态路径或完整地址;只为文件名补充存储目录。
|
export function resolveAttachmentUrl(value, type, base = getJavaApi()) {
|
if (typeof value !== 'string' || !value.trim()) throw new Error('附件地址为空')
|
let path = value.replace(/\\/g, '/')
|
base = base.replace(/\/+$/, '')
|
if (/^(blob:|data:)/i.test(path)) return path
|
if (path.startsWith(base + '/')) path = path.slice(base.length)
|
else if (/^(https?:)?\/\//i.test(path)) return new URL(path, window.location.origin).href
|
else if (/^[a-z][a-z\d+.-]*:/i.test(path)) throw new Error('附件地址格式不正确')
|
|
// 兼容已经带有 /dev-api 等代理前缀的地址,避免重复拼接。
|
const basePath = new URL(base, window.location.origin).pathname.replace(/\/$/, '')
|
if (basePath && path.startsWith(basePath + '/')) path = path.slice(basePath.length)
|
path = path.replace(/^\/+/, '')
|
if (!/^(img|word|profile)\//i.test(path)) {
|
const isImage = Number(type) === 1 || (Number(type) !== 2 && imageExtension.test(decodePart(path)))
|
path = (isImage ? 'img/' : 'word/') + path
|
}
|
// 逐段编码,保留目录和已有编码,文件名里的 #、?、空格与中文均作为文件名处理。
|
const encodedPath = path.split('/').map(part => encodeURIComponent(decodePart(part))).join('/')
|
return base + '/' + encodedPath
|
}
|
|
export function attachmentFileName(url, name) {
|
const path = new URL(url, window.location.origin).pathname
|
const storedName = decodePart(path.split('/').pop() || '')
|
const filename = (name || storedName || '附件').replace(/\\/g, '/').split('/').pop()
|
const extension = storedName.match(/\.[a-z\d]{1,10}$/i)?.[0] || ''
|
return extension && !/\.[a-z\d]{1,10}$/i.test(filename) ? filename + extension : filename
|
}
|
|
// 历史上传接口对 BMP/WebP 等格式的目录判断不一致,404 时兼容另一存储目录。
|
export function alternateImageAttachmentUrl(url) {
|
const parsed = new URL(url, window.location.origin)
|
if (!imageExtension.test(decodePart(parsed.pathname)) || !/\/(img|word)\//.test(parsed.pathname)) return null
|
parsed.pathname = parsed.pathname.replace(/\/(img|word)\//, (_, directory) => directory === 'img' ? '/word/' : '/img/')
|
return parsed.href
|
}
|
|
// 下载失败的 JSON/HTML 响应不能保存成 Word、PDF 等“损坏的附件”。
|
export async function validateAttachmentBlob(blob, filename) {
|
const head = (await blob.slice(0, 512).text()).trim()
|
if (head.startsWith('{') && blob.size < 1024 * 1024) {
|
let result
|
try {
|
result = JSON.parse(await blob.text())
|
} catch {
|
return
|
}
|
if (result && ((result.code != null && Number(result.code) !== 200) || result.success === false)) {
|
throw new Error(result.msg || result.message || '附件下载失败')
|
}
|
}
|
if (/^(<!doctype html|<html)/i.test(head) && !/\.html?$/i.test(filename)) {
|
throw new Error('未获取到附件内容,请确认文件存在或重新登录后重试')
|
}
|
}
|