新增:Markdown编辑器/数据库优化/安全修复
- Markdown 编辑器:实时预览、PDF 导出、独立查看器 - 数据库优化:动态连接池、查询缓存、Redis Pipeline - 窗口置顶功能 - 文件系统增强:右键菜单、编辑器集成、收藏夹重构 - 安全修复:XSS 防护、路径穿越、HTML 注入 - 代码质量:正则预编译、缓存锁优化、死代码清理
This commit is contained in:
@@ -5,8 +5,51 @@
|
||||
* @description 提供文件相关的通用工具函数,避免代码重复
|
||||
*/
|
||||
|
||||
import { normalizePathSeparators } from './pathHelpers.js'
|
||||
import { FILE_ICON_MAP, FILE_ICONS, BYTE_UNITS, FILE_SIZE_FORMAT, FILE_EXTENSIONS } from './constants'
|
||||
import { FILE_ICON_MAP, FILE_ICONS, BYTE_UNITS, FILE_SIZE_FORMAT } from './constants'
|
||||
|
||||
/**
|
||||
* 路径分隔符正则(匹配 Windows 和 Unix 风格)
|
||||
* @type {RegExp}
|
||||
*/
|
||||
export const PATH_SEPARATOR_REGEX = /[/\\]/
|
||||
|
||||
/**
|
||||
* 规范化路径分隔符(统一为正斜杠)
|
||||
* @param {string} path - 文件路径
|
||||
* @returns {string} 规范化后的路径
|
||||
*/
|
||||
export const normalizePathSeparators = (path) => {
|
||||
if (!path) return ''
|
||||
return path.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML 转义,防止 XSS
|
||||
* @param {string} str - 原始字符串
|
||||
* @returns {string} 转义后的字符串
|
||||
*/
|
||||
export const escapeHtml = (str) => {
|
||||
if (str == null) return ''
|
||||
return String(str)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名(路径安全)
|
||||
* @param {string} path - 文件路径
|
||||
* @returns {string} 扩展名(小写,不含点号)
|
||||
*/
|
||||
export const getExt = (path) => {
|
||||
if (!path) return ''
|
||||
const dot = path.lastIndexOf('.')
|
||||
const slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'))
|
||||
if (dot === -1 || dot < slash) return ''
|
||||
return path.slice(dot + 1).toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
@@ -46,34 +89,29 @@ export function formatBytes(bytes) {
|
||||
*/
|
||||
export function getFileName(path) {
|
||||
if (!path) return ''
|
||||
|
||||
// 后端已统一返回 / 路径,直接分割
|
||||
const parts = path.split('/')
|
||||
|
||||
const parts = path.split(PATH_SEPARATOR_REGEX)
|
||||
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') // ""
|
||||
* @returns {string[]} 路径数组
|
||||
*/
|
||||
export function getFileExtension(path) {
|
||||
if (!path) return ''
|
||||
export const splitPath = (path) => {
|
||||
if (!path) return []
|
||||
return path.split(PATH_SEPARATOR_REGEX)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名(不含扩展名)
|
||||
* @param {string} path - 文件路径
|
||||
* @returns {string} 文件名(不含扩展名)
|
||||
*/
|
||||
export const getFileNameWithoutExt = (path) => {
|
||||
const fileName = getFileName(path)
|
||||
const lastDotIndex = fileName.lastIndexOf('.')
|
||||
|
||||
if (lastDotIndex === -1 || lastDotIndex === fileName.length - 1) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return fileName.substring(lastDotIndex + 1).toLowerCase()
|
||||
const lastDot = fileName.lastIndexOf('.')
|
||||
return lastDot > 0 ? fileName.substring(0, lastDot) : fileName
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,51 +135,12 @@ export function getFileIcon(fileInfo) {
|
||||
}
|
||||
|
||||
// 获取文件扩展名
|
||||
const ext = getFileExtension(fileInfo.name)
|
||||
const ext = getExt(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 - 原始路径
|
||||
@@ -189,7 +188,7 @@ export function normalizeFilePath(path, encode = false) {
|
||||
* getFileTypeName('unknown.xyz') // "XYZ文件"
|
||||
*/
|
||||
export function getFileTypeName(path) {
|
||||
const ext = getFileExtension(path)
|
||||
const ext = getExt(path)
|
||||
const extUpper = ext.toUpperCase()
|
||||
|
||||
// 图片
|
||||
@@ -226,23 +225,6 @@ export function getFileTypeName(path) {
|
||||
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 - 文件路径
|
||||
@@ -278,6 +260,15 @@ export function joinPaths(...parts) {
|
||||
return parts.join('/').replace(/\/+/g, '/')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路径使用的分隔符(Windows 反斜杠或 Unix 正斜杠)
|
||||
* @param {string} path - 文件路径
|
||||
* @returns {string} 分隔符 '\\' 或 '/'
|
||||
*/
|
||||
export function getPathSeparator(path) {
|
||||
return path.includes('\\') ? '\\' : '/'
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父目录路径
|
||||
* @param {string} path - 文件路径
|
||||
@@ -290,14 +281,24 @@ export function joinPaths(...parts) {
|
||||
export function getParentPath(path) {
|
||||
if (!path) return ''
|
||||
|
||||
const normalizedPath = normalizeFilePath(path)
|
||||
const normalizedPath = normalizePathSeparators(path)
|
||||
const lastSlashIndex = normalizedPath.lastIndexOf('/')
|
||||
|
||||
if (lastSlashIndex <= 0) {
|
||||
return '/' // 根目录
|
||||
if (/^[A-Za-z]:$/.test(normalizedPath)) {
|
||||
return normalizedPath + '/'
|
||||
}
|
||||
return normalizedPath || '/'
|
||||
}
|
||||
|
||||
return normalizedPath.substring(0, lastSlashIndex)
|
||||
const parentPath = normalizedPath.substring(0, lastSlashIndex)
|
||||
|
||||
// 盘符根目录下文件:E:/file.txt → E:/
|
||||
if (/^[A-Za-z]:$/.test(parentPath)) {
|
||||
return parentPath + '/'
|
||||
}
|
||||
|
||||
return parentPath || '/'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user