Files
appDistribute/background/main.go

301 lines
6.7 KiB
Go
Raw Normal View History

package main
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"path/filepath"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// App 结构体定义
type App struct {
ID string `json:"id"`
Name string `json:"name"`
FileName string `json:"fileName"`
Date string `json:"date"`
FilePath string `json:"-"` // 内部使用,不序列化
}
type Doc struct {
DocBody string `json:"docBody"`
}
// 全局变量
var (
docs []Doc
apps []App
appsMutex sync.RWMutex // 读写锁,提高并发性能
filesDir = "./files"
docsDir = "./docs"
jsonFile = "./apps.json"
port = ":6903" // 修改端口号为6903
)
// 生成唯一文件名
func generateUniqueFileName(originalName string) string {
// 获取文件扩展名
ext := filepath.Ext(originalName)
// 生成基于时间、随机数和原文件名的MD5哈希
timestamp := time.Now().UnixNano()
random := rand.Int63()
hash := md5.Sum([]byte(fmt.Sprintf("%d%d%s", timestamp, random, originalName)))
// 组合成新的唯一文件名
return fmt.Sprintf("%s%s", hex.EncodeToString(hash[:]), ext)
}
// 加载apps数据
func loadApps() {
appsMutex.Lock()
defer appsMutex.Unlock()
file, err := os.Open(jsonFile)
if err != nil {
// 如果文件不存在,初始化空切片
apps = []App{}
return
}
defer file.Close()
decoder := json.NewDecoder(file)
if err := decoder.Decode(&apps); err != nil {
apps = []App{}
}
}
// 保存apps数据
func saveApps() error {
appsMutex.RLock()
defer appsMutex.RUnlock()
file, err := os.Create(jsonFile)
if err != nil {
return fmt.Errorf("failed to create apps.json: %w", err)
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(apps); err != nil {
return fmt.Errorf("failed to encode apps: %w", err)
}
return nil
}
func main() {
// 初始化随机数生成器
rand.Seed(time.Now().UnixNano())
// 创建files和docs目录
if err := os.MkdirAll(filesDir, 0755); err != nil {
panic(fmt.Sprintf("failed to create files directory: %v", err))
}
if err := os.MkdirAll(docsDir, 0755); err != nil {
panic(fmt.Sprintf("failed to create docs directory: %v", err))
}
// 加载apps数据
loadApps()
// 创建Gin引擎
// 生产环境中应该使用gin.ReleaseMode
// gin.SetMode(gin.ReleaseMode)
r := gin.Default()
// 添加CORS中间件
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
// API路由
api := r.Group("/api")
{
api.GET("/apps", getApps)
api.POST("/apps", uploadApp)
api.GET("/apps/:id", downloadApp)
api.GET("/apps/:id/", downloadApp) // 处理以/结尾的URL
api.GET("/docs", getDocs) // 新增获取文档的API端点
}
// 启动服务器
fmt.Printf("Server is running on http://localhost%s\n", port)
if err := r.Run(port); err != nil {
panic(fmt.Sprintf("failed to start server: %v", err))
}
}
// 获取App列表
func getApps(c *gin.Context) {
appsMutex.RLock()
defer appsMutex.RUnlock()
c.JSON(http.StatusOK, apps)
}
// 上传App
func uploadApp(c *gin.Context) {
// 获取表单数据
name := c.PostForm("name")
if name == "" {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "App名称不能为空"})
return
}
// 获取上传的文件
file, header, err := c.Request.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "文件上传失败: " + err.Error()})
return
}
defer file.Close()
// 生成唯一ID和文件名
id := fmt.Sprintf("%d", time.Now().UnixNano())
originalFileName := header.Filename
uniqueFileName := generateUniqueFileName(originalFileName)
filePath := filepath.Join(filesDir, uniqueFileName)
// 创建目标文件
dst, err := os.Create(filePath)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "文件保存失败: " + err.Error()})
return
}
defer dst.Close()
// 复制文件内容
if _, err = io.Copy(dst, file); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "文件复制失败: " + err.Error()})
return
}
// 添加到App列表
newApp := App{
ID: id,
Name: name,
FileName: originalFileName,
FilePath: uniqueFileName,
Date: time.Now().Format("2006-01-02 15:04:05"),
}
appsMutex.Lock()
apps = append(apps, newApp)
appsMutex.Unlock()
// 保存到文件
if err := saveApps(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "message": "保存数据失败: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"success": true})
}
// 下载指定App
func downloadApp(c *gin.Context) {
// 获取ID
id := c.Param("id")
// 查找App
appsMutex.RLock()
var targetApp *App
for i := range apps {
if apps[i].ID == id {
targetApp = &apps[i]
break
}
}
appsMutex.RUnlock()
if targetApp == nil {
c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "App不存在"})
return
}
// 构建文件路径
filePath := filepath.Join(filesDir, targetApp.FilePath)
// 检查文件是否存在
if _, err := os.Stat(filePath); os.IsNotExist(err) {
c.JSON(http.StatusNotFound, gin.H{"success": false, "message": "文件不存在"})
return
}
// 使用FileAttachment直接提供文件下载避免重定向
c.FileAttachment(filePath, targetApp.FileName)
}
// 读取文档内容
func readDocFile(docName string) (string, error) {
filePath := filepath.Join(docsDir, docName+".txt")
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
// 获取文档内容
func getDocs(c *gin.Context) {
// 读取5个文档文件
doc1, err1 := readDocFile("doc1")
if err1 != nil {
doc1 = "" // 如果文件不存在或读取失败,返回空字符串
}
doc2, err2 := readDocFile("doc2")
if err2 != nil {
doc2 = ""
}
doc3, err3 := readDocFile("doc3")
if err3 != nil {
doc3 = ""
}
doc4, err4 := readDocFile("doc4")
if err4 != nil {
doc4 = ""
}
doc5, err5 := readDocFile("doc5")
if err5 != nil {
doc5 = ""
}
if len(docs) == 0 {
docs = append(docs, Doc{DocBody: doc1})
docs = append(docs, Doc{DocBody: doc2})
docs = append(docs, Doc{DocBody: doc3})
docs = append(docs, Doc{DocBody: doc4})
docs = append(docs, Doc{DocBody: doc5})
} else {
docs[0].DocBody = doc1
docs[1].DocBody = doc2
docs[2].DocBody = doc3
docs[3].DocBody = doc4
docs[4].DocBody = doc5
}
// 返回包含message1-5的JSON
c.JSON(http.StatusOK, docs)
}