| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // Package controllers - oss.go
- //
- // ⚠️ DEPRECATED NOTICE (弃用说明)
- // ================================================================================
- // 本文件包含旧版OSS上传实现,已被 shudaooss.go 替代。
- // 建议使用 ShudaoOssController 进行文件上传操作。
- // ================================================================================
- package controllers
- import (
- "crypto/hmac"
- "crypto/sha256"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "shudao-chat-go/utils"
- "strconv"
- "time"
- "github.com/beego/beego/v2/server/web"
- )
- type OssController struct {
- web.Controller
- }
- // S3配置信息
- // S3配置信息
- var (
- accessKeyId string
- accessKeySecret string
- bucket string
- endpoint string
- region string = "raoxi" // S3区域,可根据实际情况调整
- host string
- )
- func init() {
- config := utils.GetOSSConfig()
- accessKeyId = config["access_key"]
- accessKeySecret = config["secret_key"]
- bucket = config["bucket"]
- endpoint = config["endpoint"]
- // 移除endpoint中的http://或https://前缀用于host拼接(如果需要)
- // 但原代码是: var host string = "http://" + endpoint + "/" + bucket
- // utils.GetOSSConfig返回的endpoint可能带http
- // 这里做个简单处理
- cleanEndpoint := endpoint
- if len(cleanEndpoint) > 7 && cleanEndpoint[:7] == "http://" {
- cleanEndpoint = cleanEndpoint[7:]
- } else if len(cleanEndpoint) > 8 && cleanEndpoint[:8] == "https://" {
- cleanEndpoint = cleanEndpoint[8:]
- }
- host = "http://" + cleanEndpoint + "/" + bucket
- }
- // S3服务地址 - 标准S3格式
- // var host string = "http://" + endpoint + "/" + bucket // 已在init中初始化
- // 用户上传文件时指定的前缀
- var upload_dir string = "uploads/"
- var expire_time int64 = 1800 // 30分钟
- // S3策略文档结构
- type S3PolicyDocument struct {
- Expiration string `json:"expiration"`
- Conditions []interface{} `json:"conditions"`
- }
- // S3响应结构
- type S3PolicyToken struct {
- URL string `json:"url"`
- Fields map[string]string `json:"fields"`
- Expire int64 `json:"expire"`
- StatusCode int `json:"statusCode"`
- }
- // 生成AWS4签名
- func generateAWS4Signature(secretKey, dateStamp, region, stringToSign string) string {
- kDate := hmacSHA256([]byte("AWS4"+secretKey), dateStamp)
- kRegion := hmacSHA256(kDate, region)
- kService := hmacSHA256(kRegion, "s3")
- kSigning := hmacSHA256(kService, "aws4_request")
- signature := hmacSHA256(kSigning, stringToSign)
- return hex.EncodeToString(signature)
- }
- // HMAC-SHA256计算
- func hmacSHA256(key []byte, data string) []byte {
- mac := hmac.New(sha256.New, key)
- mac.Write([]byte(data))
- return mac.Sum(nil)
- }
- func (c *OssController) Upload() {
- // 设置CORS头
- c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*")
- c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
- c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "Content-Type")
- // 处理OPTIONS预检请求
- if c.Ctx.Request.Method == "OPTIONS" {
- c.Ctx.ResponseWriter.WriteHeader(200)
- return
- }
- // 从token中获取用户信息
- userInfo, err := utils.GetUserInfoFromContext(c.Ctx.Input.GetData("userInfo"))
- if err != nil {
- c.Data["json"] = map[string]interface{}{
- "statusCode": 401,
- "error": "获取用户信息失败: " + err.Error(),
- }
- c.ServeJSON()
- return
- }
- user_id := int(userInfo.ID)
- if user_id == 0 {
- user_id = 1
- }
- // 生成时间相关字符串
- now := time.Now().UTC()
- expire_end := now.Unix() + expire_time
- dateStamp := now.Format("20060102")
- amzDate := now.Format("20060102T150405Z")
- expiration := now.Add(time.Duration(expire_time) * time.Second).Format("2006-01-02T15:04:05.000Z")
- // 生成credential
- credential := fmt.Sprintf("%s/%s/%s/s3/aws4_request", accessKeyId, dateStamp, region)
- // 生成上传目录
- uploadDir := upload_dir + now.Format("01") + now.Format("02") + "/" + strconv.Itoa(user_id) + "/"
- // 创建S3策略文档
- policy := S3PolicyDocument{
- Expiration: expiration,
- Conditions: []interface{}{
- map[string]string{"bucket": bucket},
- []interface{}{"starts-with", "$key", uploadDir},
- map[string]string{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
- map[string]string{"x-amz-credential": credential},
- map[string]string{"x-amz-date": amzDate},
- []interface{}{"content-length-range", "0", "104857600"}, // 最大100MB
- },
- }
- // 将策略文档转换为JSON并进行Base64编码
- policyJSON, err := json.Marshal(policy)
- if err != nil {
- c.Data["json"] = map[string]interface{}{
- "statusCode": 500,
- "error": "Failed to create policy",
- }
- c.ServeJSON()
- return
- }
- policyBase64 := base64.StdEncoding.EncodeToString(policyJSON)
- // 生成AWS4签名
- signature := generateAWS4Signature(accessKeySecret, dateStamp, region, policyBase64)
- // 构建表单字段
- fields := map[string]string{
- "key": uploadDir + "${filename}",
- "policy": policyBase64,
- "x-amz-algorithm": "AWS4-HMAC-SHA256",
- "x-amz-credential": credential,
- "x-amz-date": amzDate,
- "x-amz-signature": signature,
- }
- // 构建响应
- var policyToken S3PolicyToken
- policyToken.StatusCode = 200
- policyToken.URL = host
- policyToken.Fields = fields
- policyToken.Expire = expire_end
- c.Data["json"] = policyToken
- c.ServeJSON()
- }
|