XuqmGroup-RNSDK/packages/common/src/fileName.ts

44 行
1.5 KiB
TypeScript

export function fileExtension(pathOrName: string): string {
const clean = pathOrName.split(/[?#]/, 1)[0] ?? ''
const name = clean.split('/').pop() ?? clean
const index = name.lastIndexOf('.')
return index > 0 ? name.slice(index + 1).toLowerCase() : ''
}
export function sanitizeFileName(name: string, fallback = 'file'): string {
const sanitized = name
.trim()
.replace(/[\\/:*?"<>|\u0000-\u001f]/g, '_')
.replace(/\s+/g, ' ')
return sanitized || fallback
}
/** Resolve an RFC 5987/6266 filename from a Content-Disposition header. */
export function parseContentDispositionFileName(header: string): string | null {
const encoded = header.match(/filename\*=(?:[^']*'[^']*')?([^;\s]+)/i)?.[1]
if (encoded) {
try {
return sanitizeFileName(decodeURIComponent(encoded.replace(/['"]/g, '')))
} catch {
// Fall through to the non-encoded forms.
}
}
const quoted = header.match(/filename="([^"]+)"/i)?.[1]
if (quoted?.trim()) return sanitizeFileName(quoted)
const plain = header.match(/filename=([^;\s"]+)/i)?.[1]
return plain?.trim() ? sanitizeFileName(plain) : null
}
export function fileNameFromUrl(url: string, fallback = 'download'): string {
try {
const pathname = new URL(url).pathname
const candidate = pathname.split('/').filter(Boolean).at(-1)
return candidate ? sanitizeFileName(decodeURIComponent(candidate), fallback) : fallback
} catch {
const candidate = url.split(/[?#]/, 1)[0]?.split('/').filter(Boolean).at(-1)
return candidate ? sanitizeFileName(candidate, fallback) : fallback
}
}