Private
Public Access
1
0

重构:文件系统模块化架构,优化应用启动流程

This commit is contained in:
2026-01-28 00:28:54 +08:00
parent 4a9b25a505
commit 8c577f70e7
123 changed files with 32030 additions and 967 deletions

321
web/src/utils/fileUtils.js Normal file
View File

@@ -0,0 +1,321 @@
/**
* 文件工具函数集合
*
* @module utils/fileUtils
* @description 提供文件相关的通用工具函数,避免代码重复
*/
import { FILE_ICON_MAP, FILE_ICONS, BYTE_UNITS, FILE_SIZE_FORMAT, FILE_EXTENSIONS } from './constants'
/**
* 格式化文件大小
* @param {number} bytes - 文件大小(字节)
* @returns {string} 格式化后的文件大小字符串
*
* @example
* formatBytes(1024) // "1.00 KB"
* formatBytes(1048576) // "1.00 MB"
* formatBytes(0) // "0 B"
*/
export function formatBytes(bytes) {
if (!bytes || bytes === 0) return '0 B'
const unit = FILE_SIZE_FORMAT.UNIT
const decimals = FILE_SIZE_FORMAT.DECIMAL_PLACES
if (bytes < unit) return bytes + ' B'
const exp = Math.floor(Math.log(bytes) / Math.log(unit))
const value = bytes / Math.pow(unit, exp)
const unitSymbol = BYTE_UNITS[1][exp - 1] + 'B'
return value.toFixed(decimals) + ' ' + unitSymbol
}
/**
* 从文件路径中提取文件名
* @param {string} path - 文件路径
* @returns {string} 文件名
*
* @example
* getFileName('/home/user/docs/file.txt') // "file.txt"
* getFileName('C:\\Users\\user\\file.txt') // "file.txt"
* getFileName('file.txt') // "file.txt"
*/
export function getFileName(path) {
if (!path) return ''
// 统一分隔符为正斜杠
const normalizedPath = path.replace(/\\/g, '/')
// 分割路径并取最后一部分
const parts = normalizedPath.split('/')
return parts[parts.length - 1] || path
}
/**
* 从文件路径中提取文件扩展名
* @param {string} path - 文件路径
* @returns {string} 扩展名(小写,不含点号)
*
* @example
* getFileExtension('/path/to/file.txt') // "txt"
* getFileExtension('/path/to/file.TXT') // "txt"
* getFileExtension('/path/to/file') // ""
*/
export function getFileExtension(path) {
if (!path) return ''
const fileName = getFileName(path)
const lastDotIndex = fileName.lastIndexOf('.')
if (lastDotIndex === -1 || lastDotIndex === fileName.length - 1) {
return ''
}
return fileName.substring(lastDotIndex + 1).toLowerCase()
}
/**
* 根据文件信息获取对应的图标
* @param {Object} fileInfo - 文件信息对象
* @param {boolean} fileInfo.is_dir - 是否为目录
* @param {string} fileInfo.name - 文件名
* @returns {string} 文件图标emoji
*
* @example
* getFileIcon({ is_dir: true }) // "📁"
* getFileIcon({ is_dir: false, name: 'image.png' }) // "🖼️"
* getFileIcon({ is_dir: false, name: 'document.pdf' }) // "📕"
*/
export function getFileIcon(fileInfo) {
if (!fileInfo) return FILE_ICONS.FILE
// 如果是目录
if (fileInfo.is_dir) {
return FILE_ICONS.FOLDER
}
// 获取文件扩展名
const ext = getFileExtension(fileInfo.name)
// 从映射表中查找图标
return FILE_ICON_MAP.get(ext) || FILE_ICONS.FILE
}
/**
* 判断文件是否为图片
* @param {string} path - 文件路径
* @returns {boolean} 是否为图片文件
*/
export function isImageFile(path) {
const ext = getFileExtension(path)
return FILE_EXTENSIONS.IMAGE.includes(ext)
}
/**
* 判断文件是否为视频
* @param {string} path - 文件路径
* @returns {boolean} 是否为视频文件
*/
export function isVideoFile(path) {
const ext = getFileExtension(path)
return [...FILE_EXTENSIONS.VIDEO_BROWSER, ...FILE_EXTENSIONS.VIDEO_EXTERNAL].includes(ext)
}
/**
* 判断文件是否为音频
* @param {string} path - 文件路径
* @returns {boolean} 是否为音频文件
*/
export function isAudioFile(path) {
const ext = getFileExtension(path)
return FILE_EXTENSIONS.AUDIO.includes(ext)
}
/**
* 判断文件是否为PDF
* @param {string} path - 文件路径
* @returns {boolean} 是否为PDF文件
*/
export function isPdfFile(path) {
return getFileExtension(path) === 'pdf'
}
/**
* 规范化文件路径将反斜杠转换为正斜杠并进行URL编码
* @param {string} path - 原始路径
* @param {boolean} encode - 是否进行URL编码用于URL路径
* @returns {string} 规范化后的路径
*
* @example
* normalizeFilePath('C:\\Users\\user\\file.txt') // "C:/Users/user/file.txt"
* normalizeFilePath('/home/user/file.txt') // "/home/user/file.txt"
* normalizeFilePath('E:/中文路径/file.pdf', true) // "E:/%E4%B8%AD%E6%96%87%E8%B7%AF%E5%BE%84/file.pdf"
*/
export function normalizeFilePath(path, encode = false) {
if (!path) return ''
const normalized = path.replace(/\\/g, '/')
// 如果需要编码,则使用 encodeURIComponent
if (encode) {
const parts = normalized.split('/')
// 只对包含需要编码字符的路径段进行编码
// Windows 路径格式: E:/path/to/file第一部分是盘符如 E:),不应编码
return parts.map((segment, index) => {
// 盘符部分(如 "E:")不编码
if (index === 0 && /^[A-Za-z]:$/.test(segment)) {
return segment
}
// 检查是否需要编码包含非ASCII字符或特殊字符
if (/[^A-Za-z0-9\-_.~]/.test(segment)) {
return encodeURIComponent(segment)
}
return segment
}).join('/')
}
return normalized
}
/**
* 获取文件类型的友好名称
* @param {string} path - 文件路径
* @returns {string} 文件类型名称
*
* @example
* getFileTypeName('image.png') // "PNG图片"
* getFileTypeName('document.pdf') // "PDF文档"
* getFileTypeName('unknown.xyz') // "XYZ文件"
*/
export function getFileTypeName(path) {
const ext = getFileExtension(path)
const extUpper = ext.toUpperCase()
// 图片
if (['JPG', 'JPEG', 'PNG', 'GIF', 'BMP', 'SVG', 'WEBP'].includes(extUpper)) {
return `${extUpper}图片`
}
// 视频
if (['MP4', 'WEBM', 'AVI', 'MKV'].includes(extUpper)) {
return `${extUpper}视频`
}
// 音频
if (['MP3', 'WAV', 'FLAC', 'AAC'].includes(extUpper)) {
return `${extUpper}音频`
}
// PDF
if (extUpper === 'PDF') {
return 'PDF文档'
}
// 文档
if (['DOC', 'DOCX', 'XLS', 'XLSX', 'PPT', 'PPTX'].includes(extUpper)) {
return `${extUpper}文档`
}
// 代码
if (['JS', 'TS', 'PY', 'JAVA', 'GO', 'RS', 'CPP'].includes(extUpper)) {
return `${extUpper}代码`
}
// 默认返回扩展名
return ext ? `${extUpper}文件` : '文件'
}
/**
* 判断文件是否为二进制文件
* @param {string} path - 文件路径
* @returns {boolean} 是否为二进制文件
*/
export function isBinaryFile(path) {
const ext = getFileExtension(path)
const binaryExtensions = [
'exe', 'dll', 'so', 'dylib', // 可执行文件
'zip', 'rar', '7z', 'tar', 'gz', 'bz2', // 压缩文件
'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', // Office文档
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'mp3', 'mp4', // 媒体文件
'eot', 'ttf', 'otf', 'woff', 'woff2', // 字体文件
]
return binaryExtensions.includes(ext)
}
/**
* 检查路径是否为绝对路径
* @param {string} path - 文件路径
* @returns {boolean} 是否为绝对路径
*
* @example
* isAbsolutePath('C:\\Users') // true (Windows)
* isAbsolutePath('/home/user') // true (Unix)
* isAbsolutePath('folder/file') // false
*/
export function isAbsolutePath(path) {
if (!path) return false
// Windows路径盘符开头
if (/^[A-Za-z]:\\/.test(path)) return true
// Unix路径以 / 开头
if (path.startsWith('/')) return true
return false
}
/**
* 拼接路径片段
* @param {...string} parts - 路径片段
* @returns {string} 拼接后的路径
*
* @example
* joinPaths('/home', 'user', 'docs') // "/home/user/docs"
* joinPaths('C:\\Users', 'user') // "C:\\Users\\user"
*/
export function joinPaths(...parts) {
return parts.join('/').replace(/\/+/g, '/')
}
/**
* 获取父目录路径
* @param {string} path - 文件路径
* @returns {string} 父目录路径
*
* @example
* getParentPath('/home/user/docs/file.txt') // "/home/user/docs"
* getParentPath('/home/user/docs/') // "/home/user"
*/
export function getParentPath(path) {
if (!path) return ''
const normalizedPath = normalizeFilePath(path)
const lastSlashIndex = normalizedPath.lastIndexOf('/')
if (lastSlashIndex <= 0) {
return '/' // 根目录
}
return normalizedPath.substring(0, lastSlashIndex)
}
/**
* 清理文件名,移除非法字符
* @param {string} filename - 原始文件名
* @param {string} [replacement='_'] - 替换字符
* @returns {string} 清理后的文件名
*
* @example
* sanitizeFileName('file/name.txt') // "file_name.txt"
* sanitizeFileName('file:name.txt', '-') // "file-name.txt"
*/
export function sanitizeFileName(filename, replacement = '_') {
if (!filename) return ''
// Windows不允许的字符: < > : " / \ | ? *
const illegalChars = /[<>:"/\\|?*]/g
return filename.replace(illegalChars, replacement)
}