114 lines
3.0 KiB
Go
114 lines
3.0 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// LocalEngine 本地磁盘存储引擎
|
|
type LocalEngine struct {
|
|
BasePath string // 存储根目录
|
|
CDNHost string // CDN地址, 如 http://localhost:3090
|
|
CDNPathPrefix string // CDN路径前缀, 如 /public
|
|
}
|
|
|
|
// NewLocalEngine 创建本地存储引擎实例
|
|
func NewLocalEngine(basePath string) *LocalEngine {
|
|
os.MkdirAll(basePath, 0755)
|
|
return &LocalEngine{BasePath: basePath}
|
|
}
|
|
|
|
// NewLocalEngineWithCDN 创建带CDN配置的本地存储引擎
|
|
func NewLocalEngineWithCDN(basePath, cdnHost, cdnPathPrefix string) *LocalEngine {
|
|
os.MkdirAll(basePath, 0755)
|
|
return &LocalEngine{
|
|
BasePath: basePath,
|
|
CDNHost: cdnHost,
|
|
CDNPathPrefix: cdnPathPrefix,
|
|
}
|
|
}
|
|
|
|
// Upload 上传文件到本地磁盘
|
|
func (e *LocalEngine) Upload(storageKey string, reader io.Reader, size int64) error {
|
|
fullPath := filepath.Join(e.BasePath, storageKey)
|
|
dir := filepath.Dir(fullPath)
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return fmt.Errorf("创建目录失败: %w", err)
|
|
}
|
|
|
|
dst, err := os.Create(fullPath)
|
|
if err != nil {
|
|
return fmt.Errorf("创建文件失败: %w", err)
|
|
}
|
|
defer dst.Close()
|
|
|
|
if _, err := io.Copy(dst, reader); err != nil {
|
|
os.Remove(fullPath)
|
|
return fmt.Errorf("写入文件失败: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Download 从本地磁盘下载文件
|
|
func (e *LocalEngine) Download(storageKey string) (io.ReadCloser, error) {
|
|
fullPath := filepath.Join(e.BasePath, storageKey)
|
|
file, err := os.Open(fullPath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("文件不存在: %s", storageKey)
|
|
}
|
|
return nil, fmt.Errorf("打开文件失败: %w", err)
|
|
}
|
|
return file, nil
|
|
}
|
|
|
|
// Delete 从本地磁盘删除文件
|
|
func (e *LocalEngine) Delete(storageKey string) error {
|
|
fullPath := filepath.Join(e.BasePath, storageKey)
|
|
err := os.Remove(fullPath)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return fmt.Errorf("删除文件失败: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Exists 判断文件是否存在于本地磁盘
|
|
func (e *LocalEngine) Exists(storageKey string) (bool, error) {
|
|
fullPath := filepath.Join(e.BasePath, storageKey)
|
|
_, err := os.Stat(fullPath)
|
|
if err == nil {
|
|
return true, nil
|
|
}
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
// GetURL 获取文件访问URL
|
|
// 配置了CDN: http://localhost:3090/public/2026/07/02/ab/xxx.txt
|
|
// 未配置CDN: /storage/2026/07/02/ab/xxx.txt
|
|
func (e *LocalEngine) GetURL(storageKey string) string {
|
|
if e.CDNHost != "" {
|
|
// 确保路径格式正确
|
|
prefix := strings.TrimRight(e.CDNPathPrefix, "/")
|
|
key := strings.TrimLeft(storageKey, "/")
|
|
return fmt.Sprintf("%s%s/%s", e.CDNHost, prefix, key)
|
|
}
|
|
return "/storage/" + storageKey
|
|
}
|
|
|
|
// GenerateStorageKey 生成存储路径
|
|
func GenerateStorageKey(md5, extension string) string {
|
|
now := time.Now()
|
|
day := now.Format("2006/01/02")
|
|
if extension != "" {
|
|
return fmt.Sprintf("%s/%s/%s.%s", day, md5[:2], md5, extension)
|
|
}
|
|
return fmt.Sprintf("%s/%s/%s", day, md5[:2], md5)
|
|
}
|