改七牛直传
This commit is contained in:
parent
6df84b0584
commit
f84df652d9
274
controllers/qiniu_upload.go
Normal file
274
controllers/qiniu_upload.go
Normal file
@ -0,0 +1,274 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
"server/services"
|
||||
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
)
|
||||
|
||||
// QiniuUploadController 七牛云上传控制器
|
||||
type QiniuUploadController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// GetUploadToken 获取上传凭证
|
||||
// GET /platform/qiniu/token
|
||||
func (c *QiniuUploadController) GetUploadToken() {
|
||||
claims, err := c.platformClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取存储配置
|
||||
cfg, err := models.GetStorageConfig()
|
||||
if err != nil || cfg.StorageType != "qiniu" {
|
||||
c.jsonErr(400, 400, "当前未配置七牛云存储")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查配置完整性
|
||||
if cfg.QiniuAccessKey == "" || cfg.QiniuSecretKey == "" || cfg.QiniuBucket == "" {
|
||||
c.jsonErr(500, 500, "七牛云配置不完整")
|
||||
return
|
||||
}
|
||||
|
||||
// 生成文件 key(前端可以覆盖)
|
||||
datePath := time.Now().Format("2006/01/02")
|
||||
timestamp := time.Now().UnixNano()
|
||||
keyPrefix := fmt.Sprintf("%s/%d", datePath, timestamp)
|
||||
|
||||
// 创建上传策略
|
||||
mac := qbox.NewMac(cfg.QiniuAccessKey, cfg.QiniuSecretKey)
|
||||
putPolicy := storage.PutPolicy{
|
||||
Scope: cfg.QiniuBucket,
|
||||
ReturnBody: `{"key":"$(key)","hash":"$(etag)","size":$(fsize),"mimeType":"$(mimeType)"}`,
|
||||
Expires: 3600, // 1小时有效期
|
||||
}
|
||||
upToken := putPolicy.UploadToken(mac)
|
||||
|
||||
// 返回上传凭证和配置
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"token": upToken,
|
||||
"domain": cfg.QiniuDomain,
|
||||
"bucket": cfg.QiniuBucket,
|
||||
"region": cfg.QiniuRegion,
|
||||
"keyPrefix": keyPrefix,
|
||||
"expires": time.Now().Add(time.Hour).Unix(),
|
||||
"uploadUrl": getQiniuUploadURL(cfg.QiniuRegion),
|
||||
})
|
||||
}
|
||||
|
||||
// SaveFileRecord 保存文件记录
|
||||
// POST /platform/qiniu/save
|
||||
func (c *QiniuUploadController) SaveFileRecord() {
|
||||
claims, err := c.platformClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
tid := c.effectiveTid(claims)
|
||||
|
||||
// 解析请求参数
|
||||
type SaveRequest struct {
|
||||
Key string `json:"key"` // 七牛云文件 key
|
||||
Hash string `json:"hash"` // 文件 hash (etag)
|
||||
Size int64 `json:"size"` // 文件大小
|
||||
Name string `json:"name"` // 原始文件名
|
||||
MimeType string `json:"mimeType"` // 文件类型
|
||||
Cate uint64 `json:"cate"` // 分类 ID
|
||||
}
|
||||
|
||||
var req SaveRequest
|
||||
if err := c.ParseJSON(&req); err != nil {
|
||||
c.jsonErr(400, 400, "参数解析失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if req.Key == "" || req.Name == "" {
|
||||
c.jsonErr(400, 400, "缺少必填参数")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取存储配置
|
||||
cfg, err := models.GetStorageConfig()
|
||||
if err != nil || cfg.StorageType != "qiniu" {
|
||||
c.jsonErr(400, 400, "当前未配置七牛云存储")
|
||||
return
|
||||
}
|
||||
|
||||
// 构建完整 URL
|
||||
domain := strings.TrimRight(cfg.QiniuDomain, "/")
|
||||
fileURL := fmt.Sprintf("%s/%s", domain, req.Key)
|
||||
|
||||
// 计算 MD5(使用 hash 作为 MD5,或者重新计算)
|
||||
md5Sum := req.Hash
|
||||
if md5Sum == "" {
|
||||
// 如果没有 hash,使用 key 生成一个唯一标识
|
||||
h := md5.New()
|
||||
h.Write([]byte(req.Key))
|
||||
md5Sum = hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// 检查文件是否已存在(通过 MD5)
|
||||
var exist models.SystemFile
|
||||
err = models.Orm.QueryTable(new(models.SystemFile)).
|
||||
Filter("md5", md5Sum).
|
||||
Filter("tid", tid).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&exist)
|
||||
if err == nil {
|
||||
// 文件已存在,返回已有记录
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 201,
|
||||
"msg": "文件已存在",
|
||||
"data": map[string]interface{}{
|
||||
"url": exist.Src,
|
||||
"id": exist.ID,
|
||||
"name": exist.Name,
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 检测文件类型
|
||||
ext := getFileExt(req.Name)
|
||||
fileType := detectFileType(ext)
|
||||
|
||||
// 保存文件记录
|
||||
adminID := uint64(claims.UserID)
|
||||
row := &models.SystemFile{
|
||||
Tid: tid,
|
||||
Uid: &adminID,
|
||||
Name: req.Name,
|
||||
Type: fileType,
|
||||
Cate: req.Cate,
|
||||
Size: uint64(req.Size),
|
||||
Src: fileURL,
|
||||
Uploader: adminID,
|
||||
Md5: md5Sum,
|
||||
}
|
||||
|
||||
id, err := models.Orm.Insert(row)
|
||||
if err != nil {
|
||||
c.jsonErr(500, 500, "保存文件记录失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"url": fileURL,
|
||||
"id": uint64(id),
|
||||
"name": req.Name,
|
||||
"key": req.Key,
|
||||
})
|
||||
}
|
||||
|
||||
// GetStorageConfig 获取存储配置(前端用于判断上传方式)
|
||||
// GET /platform/storage/config
|
||||
func (c *QiniuUploadController) GetStorageConfig() {
|
||||
claims, err := c.platformClaims()
|
||||
if err != nil {
|
||||
c.jsonErr(401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cfg, err := models.GetStorageConfig()
|
||||
if err != nil {
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"storageType": "local",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 只返回必要的配置信息,不返回密钥
|
||||
c.jsonOK(map[string]interface{}{
|
||||
"storageType": cfg.StorageType,
|
||||
"qiniuDomain": cfg.QiniuDomain,
|
||||
"qiniuRegion": cfg.QiniuRegion,
|
||||
})
|
||||
}
|
||||
|
||||
// getQiniuUploadURL 根据区域获取上传地址
|
||||
func getQiniuUploadURL(region string) string {
|
||||
switch region {
|
||||
case "z0":
|
||||
return "https://up-z0.qiniup.com"
|
||||
case "z1":
|
||||
return "https://up-z1.qiniup.com"
|
||||
case "z2":
|
||||
return "https://up-z2.qiniup.com"
|
||||
case "na0":
|
||||
return "https://up-na0.qiniup.com"
|
||||
case "as0":
|
||||
return "https://up-as0.qiniup.com"
|
||||
case "cn-east-2":
|
||||
return "https://up-cn-east-2.qiniup.com"
|
||||
default:
|
||||
return "https://up-z0.qiniup.com" // 默认华东
|
||||
}
|
||||
}
|
||||
|
||||
// getFileExt 获取文件扩展名
|
||||
func getFileExt(filename string) string {
|
||||
parts := strings.Split(filename, ".")
|
||||
if len(parts) > 1 {
|
||||
return strings.ToLower(parts[len(parts)-1])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// detectFileType 检测文件类型
|
||||
func detectFileType(ext string) uint8 {
|
||||
imageExts := map[string]bool{
|
||||
"jpg": true, "jpeg": true, "png": true, "gif": true, "bmp": true,
|
||||
"webp": true, "svg": true, "ico": true,
|
||||
}
|
||||
videoExts := map[string]bool{
|
||||
"mp4": true, "avi": true, "mov": true, "wmv": true, "flv": true,
|
||||
"mkv": true, "webm": true, "m4v": true,
|
||||
}
|
||||
audioExts := map[string]bool{
|
||||
"mp3": true, "wav": true, "flac": true, "aac": true, "ogg": true,
|
||||
"m4a": true, "wma": true,
|
||||
}
|
||||
docExts := map[string]bool{
|
||||
"doc": true, "docx": true, "xls": true, "xlsx": true, "ppt": true,
|
||||
"pptx": true, "pdf": true, "txt": true, "md": true,
|
||||
}
|
||||
archiveExts := map[string]bool{
|
||||
"zip": true, "rar": true, "7z": true, "tar": true, "gz": true,
|
||||
"bz2": true, "xz": true,
|
||||
}
|
||||
executableExts := map[string]bool{
|
||||
"exe": true, "msi": true, "dmg": true, "pkg": true, "deb": true,
|
||||
"rpm": true, "apk": true, "msix": true,
|
||||
}
|
||||
|
||||
if imageExts[ext] {
|
||||
return 1 // 图片
|
||||
}
|
||||
if videoExts[ext] {
|
||||
return 2 // 视频
|
||||
}
|
||||
if audioExts[ext] {
|
||||
return 3 // 音频
|
||||
}
|
||||
if docExts[ext] {
|
||||
return 4 // 文档
|
||||
}
|
||||
if archiveExts[ext] || executableExts[ext] {
|
||||
return 5 // 压缩包/安装包
|
||||
}
|
||||
return 0 // 其他
|
||||
}
|
||||
@ -13,6 +13,12 @@ chmod +x install-systemd-service.sh
|
||||
|
||||
# 运行安装脚本
|
||||
sudo bash install-systemd-service.sh
|
||||
|
||||
或者
|
||||
|
||||
sudo env PATH=$PATH:/usr/local/btgo/bin bash install-systemd-service.sh
|
||||
|
||||
|
||||
```
|
||||
|
||||
脚本会自动:
|
||||
|
||||
@ -152,4 +152,9 @@ func Register() {
|
||||
beego.Router("/platform/batchdeletefiles", &controllers.PlatformFileController{}, "post:BatchDeleteFiles")
|
||||
beego.Router("/platform/batchDeleteFilesPermanently", &controllers.PlatformFileController{}, "post:BatchDeleteFilesPermanently")
|
||||
beego.Router("/platform/batchMoveFiles", &controllers.PlatformFileController{}, "post:BatchMoveFiles")
|
||||
|
||||
// 七牛云直传相关
|
||||
beego.Router("/platform/storage/config", &controllers.QiniuUploadController{}, "get:GetStorageConfig")
|
||||
beego.Router("/platform/qiniu/token", &controllers.QiniuUploadController{}, "get:GetUploadToken")
|
||||
beego.Router("/platform/qiniu/save", &controllers.QiniuUploadController{}, "post:SaveFileRecord")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user