Files
nl-blogs/server/utils/oss.go
2026-01-19 16:14:08 +08:00

139 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"fmt"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"
"time"
)
// StorageType 存储类型
type StorageType string
const (
StorageLocal StorageType = "local"
StorageQCloud StorageType = "qcloud"
StorageAliyun StorageType = "aliyun"
StorageQiniu StorageType = "qiniu"
)
// OSSConfig OSS配置
type OSSConfig struct {
StorageType string
AccessKey string
SecretKey string
Bucket string
Region string
Domain string
}
// OSSUploader OSS上传接口
type OSSUploader interface {
Upload(file multipart.File, filename string, size int64) (string, string, error) // 返回 filePath, fileURL, error
Delete(filePath string) error
}
// GetOSSUploader 根据存储类型获取上传器
func GetOSSUploader(config *OSSConfig) (OSSUploader, error) {
switch StorageType(config.StorageType) {
case StorageLocal:
return &LocalUploader{
BasePath: "./uploads",
BaseURL: "/uploads",
}, nil
case StorageQCloud:
// TODO: 实现腾讯云COS上传
return nil, fmt.Errorf("qcloud storage not implemented yet")
case StorageAliyun:
// TODO: 实现阿里云OSS上传
return nil, fmt.Errorf("aliyun storage not implemented yet")
case StorageQiniu:
// TODO: 实现七牛云上传
return nil, fmt.Errorf("qiniu storage not implemented yet")
default:
return nil, fmt.Errorf("unsupported storage type: %s", config.StorageType)
}
}
// LocalUploader 本地存储上传器
type LocalUploader struct {
BasePath string
BaseURL string
}
// Upload 上传文件到本地
func (l *LocalUploader) Upload(file multipart.File, filename string, size int64) (string, string, error) {
// 生成唯一文件名
ext := filepath.Ext(filename)
timestamp := time.Now().Unix()
randomStr := fmt.Sprintf("%d", timestamp)
newFilename := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filename, ext), randomStr, ext)
// 按日期创建目录
dateDir := time.Now().Format("2006/01/02")
uploadDir := filepath.Join(l.BasePath, dateDir)
// 创建目录
if err := os.MkdirAll(uploadDir, 0755); err != nil {
return "", "", fmt.Errorf("failed to create upload directory: %v", err)
}
// 完整文件路径
filePath := filepath.Join(uploadDir, newFilename)
// 创建目标文件
dst, err := os.Create(filePath)
if err != nil {
return "", "", fmt.Errorf("failed to create file: %v", err)
}
defer dst.Close()
// 复制文件内容
if _, err := io.Copy(dst, file); err != nil {
return "", "", fmt.Errorf("failed to copy file: %v", err)
}
// 生成访问URL
fileURL := fmt.Sprintf("%s/%s/%s", l.BaseURL, dateDir, newFilename)
return filePath, fileURL, nil
}
// Delete 删除本地文件
func (l *LocalUploader) Delete(filePath string) error {
// 确保文件路径在BasePath内安全措施
absBasePath, err := filepath.Abs(l.BasePath)
if err != nil {
return err
}
absFilePath, err := filepath.Abs(filePath)
if err != nil {
return err
}
if !strings.HasPrefix(absFilePath, absBasePath) {
return fmt.Errorf("invalid file path: outside base directory")
}
return os.Remove(filePath)
}
// GetFileType 根据MIME类型判断文件类型
func GetFileType(mimeType string) string {
if strings.HasPrefix(mimeType, "image/") {
return "image"
} else if strings.HasPrefix(mimeType, "video/") {
return "video"
} else if strings.HasPrefix(mimeType, "application/pdf") ||
strings.HasPrefix(mimeType, "application/msword") ||
strings.HasPrefix(mimeType, "application/vnd.openxmlformats") ||
strings.HasPrefix(mimeType, "text/") {
return "document"
}
return "other"
}