You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

356 lines
9.8 KiB

// MIT License
//
// Copyright (c) 2024 godoos.com
// Email: xpbb@qq.com
// GitHub: github.com/phpk/godoos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package files
import (
"crypto/md5"
"encoding/hex"
"fmt"
"godo/libs"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"time"
)
// Common response structure
type OsFileInfo struct {
IsFile bool `json:"isFile"`
IsDir bool `json:"isDirectory"`
IsSymlink bool `json:"isSymlink"`
Size int64 `json:"size"`
ModTime time.Time `json:"modTime"`
AccessTime time.Time `json:"atime"`
CreateTime time.Time `json:"birthtime"`
Mode os.FileMode `json:"mode"`
Name string `json:"name"` // 文件名
Path string `json:"path"` // 文件路径
OldPath string `json:"oldPath"` // 旧的文件路径
ParentPath string `json:"parentPath"` // 父目录路径
Content string `json:"content"` // 文件内容
Ext string `json:"ext"` // 文件扩展名
Title string `json:"title"` // 文件名(不包含扩展名)
ID int `json:"id,omitempty"` // 文件ID(可选)
}
// validateFilePath 验证路径不为空
func validateFilePath(path string) error {
if path == "" {
return fmt.Errorf("empty path")
}
return nil
}
// validateFilePathPair 验证源路径和目标路径不为空
func validateFilePathPair(src, dst string) error {
if err := validateFilePath(src); err != nil {
return err
}
if err := validateFilePath(dst); err != nil {
return err
}
return nil
}
// ReadDir reads a directory and returns a list of files
func ReadDir(basePath, path string) ([]os.DirEntry, error) {
fullPath := filepath.Join(basePath, path)
return os.ReadDir(fullPath)
}
// Stat retrieves file information
func Stat(basePath, path string) (os.FileInfo, error) {
fullPath := filepath.Join(basePath, path)
return os.Stat(fullPath)
}
// Exists checks if a file or directory exists
func Exists(basePath, path string) bool {
fullPath := filepath.Join(basePath, path)
// _, err := os.Stat(fullPath)
// return err == nil
return libs.PathExists(fullPath)
}
// ReadFile reads a file's content
func ReadFile(basePath, path string) ([]byte, error) {
fullPath := filepath.Join(basePath, path)
return os.ReadFile(fullPath)
}
// Unlink removes a file
func Unlink(basePath, path string) error {
fullPath := filepath.Join(basePath, path)
return os.Remove(fullPath)
}
// Clear removes the entire filesystem
func Clear(basePath string) error {
return os.RemoveAll(basePath)
}
// Rename renames a file
func Rename(basePath, oldPath, newPath string) error {
return os.Rename(filepath.Join(basePath, oldPath), filepath.Join(basePath, newPath))
}
// Mkdir creates a directory
func Mkdir(basePath, dirPath string) error {
fullPath := filepath.Join(basePath, dirPath)
return os.MkdirAll(fullPath, 0755)
}
// Rmdir removes a directory
func Rmdir(basePath, dirPath string) error {
fullPath := filepath.Join(basePath, dirPath)
return os.RemoveAll(fullPath)
}
func CopyResource(src, dst string) error {
// 检查源路径是否为目录或文件
info, err := os.Stat(src)
if err != nil {
return err
}
// 如果源路径是一个目录,则递归复制整个目录
if info.IsDir() {
return CopyDirectory(src, dst)
}
// 如果源路径是一个文件,则直接复制文件
return CopyFile(src, dst)
}
func CopyDirectory(src, dst string) error {
// 创建目标目录
if err := os.MkdirAll(dst, os.ModePerm); err != nil {
return err
}
// 遍历源目录中的所有文件和子目录
files, err := os.ReadDir(src)
if err != nil {
return err
}
for _, file := range files {
// 构建源文件和目标文件的完整路径
srcPath := filepath.Join(src, file.Name())
dstPath := filepath.Join(dst, file.Name())
// 递归复制子目录或复制文件
if file.IsDir() {
if err := CopyDirectory(srcPath, dstPath); err != nil {
return err
}
} else {
if err := CopyFile(srcPath, dstPath); err != nil {
return err
}
}
}
return nil
}
// CopyFile copies a file
func CopyFile(src, dst string) error {
sf, err := os.Open(src)
if err != nil {
return err
}
defer sf.Close()
df, err := os.Create(dst)
if err != nil {
return err
}
defer df.Close()
_, err = io.Copy(df, sf)
if err != nil {
return err
}
err = df.Sync()
if err != nil {
return err
}
return df.Close()
}
// WriteFile writes content to a file
func WriteFile(filePath string, content []byte, perm os.FileMode) error {
return os.WriteFile(filePath, content, perm)
}
// AppendToFile appends content to a file
// AppendToFile appends the given content to the specified file.
// It assumes that 'content' is an io.Reader representing the data to append.
func AppendToFile(filePath string, content io.Reader) error {
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
// 文件不存在或打开时发生其他错误
return err
}
defer file.Close()
// 将内容写入文件
if _, err := io.Copy(file, content); err != nil {
return err
}
return nil
}
func Chmod(basePath string, path string, mode os.FileMode) error {
fullPath := filepath.Join(basePath, path)
return os.Chmod(fullPath, mode)
}
// GetFileInfo 从fs.DirEntry或路径获取文件信息
func GetFileInfo(entry interface{}, basePath, parentPath string) (*OsFileInfo, error) {
var fileInfo os.FileInfo
var err error
var filePath string
switch v := entry.(type) {
case fs.DirEntry:
// 如果传入的是fs.DirEntry
fileInfo, err = v.Info()
if err != nil {
return nil, fmt.Errorf("failed to get file info from DirEntry: %v", err)
}
filePath = filepath.Join(basePath, parentPath, fileInfo.Name())
case string:
// 如果传入的是路径字符串
filePath = filepath.Join(basePath, parentPath, v)
fileInfo, err = os.Stat(filePath)
if err != nil {
return nil, fmt.Errorf("failed to stat file from path: %v", err)
}
default:
return nil, fmt.Errorf("unsupported type provided to GetFileInfo, expected fs.DirEntry or string")
}
osFileInfo := &OsFileInfo{
Name: fileInfo.Name(),
Path: filePath, // 根据实际情况调整,可能需要使用之前构造的完整路径filePath
}
// 初始化其他字段,如IsFile, IsDir, Size等
osFileInfo.ID = 0
osFileInfo.IsFile = !fileInfo.IsDir()
osFileInfo.IsDir = fileInfo.IsDir()
osFileInfo.Size = fileInfo.Size()
osFileInfo.ModTime = fileInfo.ModTime()
osFileInfo.AccessTime = fileInfo.ModTime() // 注意:os.FileInfo没有直接提供访问时间,这里用修改时间代替
osFileInfo.CreateTime = fileInfo.ModTime() // 同上,仅作示例
osFileInfo.Mode = fileInfo.Mode()
osFileInfo.Path = strings.TrimPrefix(filePath, basePath)
osFileInfo.OldPath = osFileInfo.Path
osFileInfo.ParentPath = parentPath
osFileInfo.Title = strings.TrimSuffix(osFileInfo.Name, filepath.Ext(osFileInfo.Name))
osFileInfo.Ext = strings.TrimPrefix(filepath.Ext(osFileInfo.Name), ".")
return osFileInfo, nil
}
func GetDesktopPath() string {
return filepath.Join("C", "Users", "Desktop")
}
// RemoveFirstSlashOrBackslash 检查字符串的第一个字符是否为 '/' 或 '\'
// 如果是,则返回去掉该字符的剩余部分;如果不是,则返回原字符串。
func RemoveFirstSlashOrBackslash(s string) string {
if len(s) > 0 && (s[0] == '/' || s[0] == '\\') {
return s[1:]
}
return s
}
func CheckAddDesktop(filePath string) error {
checkPath := RemoveFirstSlashOrBackslash(filepath.Dir(filePath))
if checkPath != GetDesktopPath() {
return fmt.Errorf("文件不在桌面目录下")
}
basePath, err := libs.GetOsDir()
if err != nil {
return fmt.Errorf("获取系统目录失败")
}
if !Exists(basePath, filePath) {
return fmt.Errorf("文件不存在")
}
osFileInfo, err := GetFileInfo(filePath, basePath, "")
if err != nil {
return fmt.Errorf("获取文件信息失败")
}
err = AddDesktop(*osFileInfo, "Desktop")
if err != nil {
return fmt.Errorf("添加桌面失败")
}
return nil
}
func CheckDeleteDesktop(filePath string) error {
checkPath := RemoveFirstSlashOrBackslash(filepath.Dir(filePath))
if checkPath != GetDesktopPath() {
return fmt.Errorf("文件不在桌面目录下")
}
basePath, err := libs.GetOsDir()
if err != nil {
return fmt.Errorf("获取系统目录失败")
}
if !Exists(basePath, filePath) {
return fmt.Errorf("文件不存在")
}
osFileInfo, err := GetFileInfo(filePath, basePath, "")
if err != nil {
return fmt.Errorf("获取文件信息失败")
}
err = DeleteDesktop(osFileInfo.Name)
if err != nil {
return fmt.Errorf("添加桌面失败")
}
return nil
}
// 校验文件密码
func CheckFilePwd(fpwd string) bool {
mhash := md5.New()
mhash.Write([]byte(fpwd))
v := mhash.Sum(nil)
pwdstr := hex.EncodeToString(v)
oldpwd, _ := libs.GetConfig("filepwd")
return oldpwd == pwdstr
}
func IsHavePwd(pwd string) bool {
if len(pwd) > 0 {
return true
} else {
return false
}
}