2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 05:31:44 +08:00
Files
panel/pkg/storage/local.go
2026-01-22 03:44:20 +08:00

145 lines
2.8 KiB
Go

package storage
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/shirou/gopsutil/v4/disk"
pkgio "github.com/acepanel/panel/pkg/io"
)
type Local struct {
basePath string
}
func NewLocal(basePath string) (Storage, error) {
if basePath == "" {
return nil, errors.New("base path is empty")
}
return &Local{
basePath: basePath,
}, nil
}
// Delete 删除文件
func (l *Local) Delete(files ...string) error {
for _, file := range files {
fullPath := l.fullPath(file)
if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) {
return err
}
}
return nil
}
// Exists 检查文件是否存在
func (l *Local) Exists(file string) bool {
fullPath := l.fullPath(file)
_, err := os.Stat(fullPath)
return !os.IsNotExist(err)
}
// LastModified 获取文件最后修改时间
func (l *Local) LastModified(file string) (time.Time, error) {
fullPath := l.fullPath(file)
info, err := os.Stat(fullPath)
if err != nil {
return time.Time{}, err
}
return info.ModTime(), nil
}
// List 列出目录下的所有文件
func (l *Local) List(path string) ([]string, error) {
fullPath := l.fullPath(path)
entries, err := os.ReadDir(fullPath)
if err != nil {
return nil, err
}
var files []string
for _, entry := range entries {
if !entry.IsDir() {
files = append(files, entry.Name())
}
}
return files, nil
}
// Put 写入文件内容
func (l *Local) Put(file string, content io.Reader) error {
fullPath := l.fullPath(file)
// 确保目录存在
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
return err
}
// 预检查空间
if err := l.preCheckPath(filepath.Dir(fullPath)); err != nil {
return fmt.Errorf("pre check path failed: %w", err)
}
f, err := os.OpenFile(fullPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer func(f *os.File) { _ = f.Close() }(f)
_, err = io.Copy(f, content)
return err
}
// Size 获取文件大小
func (l *Local) Size(file string) (int64, error) {
fullPath := l.fullPath(file)
info, err := os.Stat(fullPath)
if err != nil {
return 0, err
}
return info.Size(), nil
}
func (l *Local) fullPath(path string) string {
path = strings.TrimPrefix(path, "/")
if path == "" {
return l.basePath
}
if filepath.IsAbs(path) {
return path
}
return filepath.Join(l.basePath, path)
}
func (l *Local) preCheckPath(path string) error {
size, err := pkgio.SizeX(path)
if err != nil {
return err
}
files, err := pkgio.CountX(path)
if err != nil {
return err
}
usage, err := disk.Usage(l.basePath)
if err != nil {
return err
}
if uint64(size) > usage.Free {
return errors.New("insufficient backup directory space")
}
if uint64(files) > usage.InodesFree {
return errors.New("insufficient backup directory inode")
}
return nil
}