mirror of
https://github.com/MatrixSeven/file-transfer-go.git
synced 2026-02-15 01:34:46 +08:00
169 lines
3.8 KiB
Go
169 lines
3.8 KiB
Go
package services
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"fmt"
|
||
"io"
|
||
"mime/multipart"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"time"
|
||
|
||
"chuan/internal/models"
|
||
|
||
"github.com/google/uuid"
|
||
)
|
||
|
||
type FileService struct {
|
||
uploadDir string
|
||
}
|
||
|
||
func NewFileService() *FileService {
|
||
return &FileService{
|
||
uploadDir: "./uploads",
|
||
}
|
||
}
|
||
|
||
// SaveFile 保存上传的文件
|
||
func (fs *FileService) SaveFile(file multipart.File, header *multipart.FileHeader) (*models.FileInfo, error) {
|
||
// 生成唯一文件ID
|
||
fileID := uuid.New().String()
|
||
|
||
// 生成取件码
|
||
code := fs.generateCode()
|
||
|
||
// 创建文件路径
|
||
fileExt := filepath.Ext(header.Filename)
|
||
fileName := fmt.Sprintf("%s%s", fileID, fileExt)
|
||
filePath := filepath.Join(fs.uploadDir, fileName)
|
||
|
||
// 确保上传目录存在
|
||
if err := os.MkdirAll(fs.uploadDir, 0755); err != nil {
|
||
return nil, fmt.Errorf("创建上传目录失败: %v", err)
|
||
}
|
||
|
||
// 创建目标文件
|
||
dst, err := os.Create(filePath)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建文件失败: %v", err)
|
||
}
|
||
defer dst.Close()
|
||
|
||
// 复制文件内容
|
||
size, err := io.Copy(dst, file)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("保存文件失败: %v", err)
|
||
}
|
||
|
||
// 获取文件内容类型
|
||
contentType := header.Header.Get("Content-Type")
|
||
if contentType == "" {
|
||
contentType = fs.getContentType(header.Filename)
|
||
}
|
||
|
||
fileInfo := &models.FileInfo{
|
||
ID: fileID,
|
||
FileName: header.Filename,
|
||
FileSize: size,
|
||
ContentType: contentType,
|
||
Code: code,
|
||
UploadTime: time.Now(),
|
||
ExpiryTime: time.Now().Add(24 * time.Hour), // 24小时过期
|
||
FilePath: filePath,
|
||
DownloadURL: fmt.Sprintf("/download/%s", code),
|
||
}
|
||
|
||
// 存储文件信息到内存(生产环境应使用Redis)
|
||
store := GetStore()
|
||
if err := store.StoreFileInfo(fileInfo); err != nil {
|
||
return nil, fmt.Errorf("存储文件信息失败: %v", err)
|
||
}
|
||
|
||
return fileInfo, nil
|
||
}
|
||
|
||
// generateCode 生成6位取件码
|
||
func (fs *FileService) generateCode() string {
|
||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||
b := make([]byte, 6)
|
||
rand.Read(b)
|
||
for i := range b {
|
||
b[i] = charset[b[i]%byte(len(charset))]
|
||
}
|
||
return string(b)
|
||
}
|
||
|
||
// getContentType 根据文件扩展名获取内容类型
|
||
func (fs *FileService) getContentType(filename string) string {
|
||
ext := strings.ToLower(filepath.Ext(filename))
|
||
switch ext {
|
||
case ".pdf":
|
||
return "application/pdf"
|
||
case ".epub":
|
||
return "application/epub+zip"
|
||
case ".mobi":
|
||
return "application/x-mobipocket-ebook"
|
||
case ".txt":
|
||
return "text/plain"
|
||
case ".jpg", ".jpeg":
|
||
return "image/jpeg"
|
||
case ".png":
|
||
return "image/png"
|
||
case ".gif":
|
||
return "image/gif"
|
||
case ".mp4":
|
||
return "video/mp4"
|
||
case ".avi":
|
||
return "video/avi"
|
||
case ".mov":
|
||
return "video/quicktime"
|
||
case ".zip":
|
||
return "application/zip"
|
||
case ".rar":
|
||
return "application/x-rar-compressed"
|
||
case ".7z":
|
||
return "application/x-7z-compressed"
|
||
default:
|
||
return "application/octet-stream"
|
||
}
|
||
}
|
||
|
||
// GetFileByCode 根据取件码获取文件信息
|
||
func (fs *FileService) GetFileByCode(code string) (*models.FileInfo, error) {
|
||
store := GetStore()
|
||
return store.GetFileInfo(code)
|
||
}
|
||
|
||
// DeleteFile 删除文件
|
||
func (fs *FileService) DeleteFile(code string) error {
|
||
fileInfo, err := fs.GetFileByCode(code)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 删除物理文件
|
||
if err := os.Remove(fileInfo.FilePath); err != nil {
|
||
return fmt.Errorf("删除文件失败: %v", err)
|
||
}
|
||
|
||
// 从内存存储删除文件信息
|
||
store := GetStore()
|
||
store.DeleteFileInfo(code)
|
||
|
||
return nil
|
||
}
|
||
|
||
// ConvertEpubToMobi 将EPUB转换为MOBI格式
|
||
func (fs *FileService) ConvertEpubToMobi(epubPath string) (string, error) {
|
||
// TODO: 集成Calibre API进行格式转换
|
||
// 这里暂时返回原文件路径
|
||
return epubPath, fmt.Errorf("格式转换功能尚未实现")
|
||
}
|
||
|
||
// CleanExpiredFiles 清理过期文件
|
||
func (fs *FileService) CleanExpiredFiles() error {
|
||
// TODO: 实现定期清理过期文件的逻辑
|
||
return nil
|
||
}
|