功能: - 新增 PathBreadcrumb 组件,支持路径快速跳转 - 新增 DropdownItem 通用下拉菜单组件 优化: - 版本升级流程优化(Pinia 状态管理、进度节流、完整下载验证) - 模块延迟初始化(数据库、文件系统按需启动) - API 数据格式统一(蛇形转驼峰) - CodeMirror 语言包按需动态加载 - Markdown 渲染增强(支持锚点跳转) 重构: - 迁移到 Pinia 状态管理(stores/config.ts、stores/theme.ts、stores/update.ts) - 简化 UpdatePanel、UpdateNotification、ThemeToggle 逻辑 - 优化表结构加载逻辑 清理: - 删除测试组件 index-simple.vue - 删除旧的 useTheme.ts
122 lines
3.4 KiB
Go
122 lines
3.4 KiB
Go
package filesystem
|
||
|
||
import (
|
||
"archive/zip"
|
||
"fmt"
|
||
"io"
|
||
"path/filepath"
|
||
)
|
||
|
||
// ZipOperation ZIP操作回调函数类型
|
||
// 用于 withZipReader 通用包装器
|
||
type ZipOperation func(*zip.ReadCloser) (interface{}, error)
|
||
|
||
// withZipReader 通用的ZIP文件操作包装器
|
||
// 消除重复的打开/关闭逻辑,统一错误处理
|
||
// 参数:
|
||
// - zipPath: ZIP文件路径
|
||
// - operation: 操作回调函数,接收 *zip.ReadCloser,返回任意结果
|
||
//
|
||
// 返回:
|
||
// - interface{}: 操作结果
|
||
// - error: 错误信息
|
||
func withZipReader(zipPath string, operation ZipOperation) (interface{}, error) {
|
||
// 1. 统一验证路径
|
||
if err := validateZipPath(zipPath); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 2. 打开 ZIP 文件
|
||
reader, err := zip.OpenReader(zipPath)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("打开 zip 文件失败: %v", err)
|
||
}
|
||
defer reader.Close()
|
||
|
||
// 3. 执行操作
|
||
result, err := operation(reader)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// withZipFile 在ZIP文件中查找特定文件并执行操作
|
||
// 进一步封装,用于处理单个文件的操作
|
||
type ZipFileOperation func(*zip.File) (interface{}, error)
|
||
|
||
// withZipFile 在ZIP中查找文件并执行操作
|
||
func withZipFile(zipPath, filePath string, operation ZipFileOperation) (interface{}, error) {
|
||
return withZipReader(zipPath, func(reader *zip.ReadCloser) (interface{}, error) {
|
||
// 查找目标文件
|
||
for _, file := range reader.File {
|
||
if isMatchFile(file, filePath) {
|
||
return operation(file)
|
||
}
|
||
}
|
||
return nil, fmt.Errorf("文件在 zip 中不存在: %s", filePath)
|
||
})
|
||
}
|
||
|
||
// isMatchFile 检查文件是否匹配目标路径
|
||
func isMatchFile(file *zip.File, targetPath string) bool {
|
||
return file.Name == targetPath ||
|
||
filepath.Clean(file.Name) == filepath.Clean(targetPath)
|
||
}
|
||
|
||
// openZipFileInReader 在ZIP reader中打开指定文件
|
||
// 用于读取文件内容的辅助函数
|
||
func openZipFileInReader(reader *zip.ReadCloser, filePath string) (io.ReadCloser, *zip.File, error) {
|
||
for _, file := range reader.File {
|
||
if isMatchFile(file, filePath) {
|
||
if file.Mode().IsDir() {
|
||
return nil, nil, fmt.Errorf("不能读取目录")
|
||
}
|
||
|
||
rc, err := file.Open()
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("打开 zip 中的文件失败: %v", err)
|
||
}
|
||
return rc, file, nil
|
||
}
|
||
}
|
||
return nil, nil, fmt.Errorf("文件在 zip 中不存在: %s", filePath)
|
||
}
|
||
|
||
// readAllFromFile 从文件读取所有内容
|
||
// 辅助函数,避免重复的 io.ReadAll 调用
|
||
func readAllFromFile(rc io.ReadCloser) ([]byte, error) {
|
||
defer rc.Close()
|
||
return io.ReadAll(rc)
|
||
}
|
||
|
||
// getCompressionMethodString 获取压缩方法字符串描述
|
||
func getCompressionMethodString(method uint16) string {
|
||
if method == 8 {
|
||
return "Deflate"
|
||
}
|
||
return "Store"
|
||
}
|
||
|
||
// createFileInfoMap 创建文件信息map(通用格式)
|
||
func createFileInfoMap(file *zip.File, includeExtra ...bool) map[string]interface{} {
|
||
info := map[string]interface{}{
|
||
"name": filepath.Base(file.Name),
|
||
"path": file.Name, // zip 中的路径(已使用 /)
|
||
"is_dir": file.Mode().IsDir(),
|
||
"size": file.UncompressedSize64,
|
||
"compressed": file.CompressedSize64,
|
||
"mod_time": file.Modified.Format("2006-01-02 15:04:05"),
|
||
"method": getCompressionMethodString(file.Method),
|
||
}
|
||
|
||
// 可选:额外信息
|
||
if len(includeExtra) > 0 && includeExtra[0] {
|
||
info["mode"] = file.Mode().String()
|
||
info["comment"] = file.Comment
|
||
}
|
||
|
||
return info
|
||
}
|