一阶段ok
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// AttachmentStorage handles file operations for email attachments on disk.
|
||||
type AttachmentStorage struct {
|
||||
baseDir string // cfg.Storage.AttachDir
|
||||
}
|
||||
|
||||
// NewAttachmentStorage creates a new AttachmentStorage with the given base directory.
|
||||
func NewAttachmentStorage(baseDir string) *AttachmentStorage {
|
||||
return &AttachmentStorage{baseDir: baseDir}
|
||||
}
|
||||
|
||||
// Save writes attachment data to disk and returns the relative file path.
|
||||
// The filename is generated as {uuid}{ext} to avoid collisions.
|
||||
func (s *AttachmentStorage) Save(filename string, data []byte) (string, error) {
|
||||
// Ensure the base directory exists
|
||||
if err := os.MkdirAll(s.baseDir, 0755); err != nil {
|
||||
return "", fmt.Errorf("创建附件目录失败: %w", err)
|
||||
}
|
||||
|
||||
// Generate a unique filename with the original extension
|
||||
ext := filepath.Ext(filename)
|
||||
uniqueName := uuid.New().String() + ext
|
||||
|
||||
fullPath := filepath.Join(s.baseDir, uniqueName)
|
||||
if err := os.WriteFile(fullPath, data, 0644); err != nil {
|
||||
return "", fmt.Errorf("写入附件文件失败: %w", err)
|
||||
}
|
||||
|
||||
return uniqueName, nil
|
||||
}
|
||||
|
||||
// Read reads attachment data from disk given a relative path.
|
||||
func (s *AttachmentStorage) Read(relPath string) ([]byte, error) {
|
||||
fullPath := s.FullPath(relPath)
|
||||
data, err := os.ReadFile(fullPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取附件文件失败: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Delete removes an attachment file from disk given a relative path.
|
||||
func (s *AttachmentStorage) Delete(relPath string) error {
|
||||
fullPath := s.FullPath(relPath)
|
||||
if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("删除附件文件失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FullPath returns the absolute path for a given relative path.
|
||||
func (s *AttachmentStorage) FullPath(relPath string) string {
|
||||
// Prevent directory traversal attacks
|
||||
cleanRel := filepath.Clean(relPath)
|
||||
if strings.HasPrefix(cleanRel, "..") {
|
||||
cleanRel = strings.TrimPrefix(cleanRel, "../")
|
||||
}
|
||||
return filepath.Join(s.baseDir, cleanRel)
|
||||
}
|
||||
Reference in New Issue
Block a user