oss.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Package controllers - oss.go
  2. //
  3. // ⚠️ DEPRECATED NOTICE (弃用说明)
  4. // ================================================================================
  5. // 本文件包含旧版OSS上传实现,已被 shudaooss.go 替代。
  6. // 建议使用 ShudaoOssController 进行文件上传操作。
  7. // ================================================================================
  8. package controllers
  9. import (
  10. "crypto/hmac"
  11. "crypto/sha256"
  12. "encoding/base64"
  13. "encoding/hex"
  14. "encoding/json"
  15. "fmt"
  16. "shudao-chat-go/utils"
  17. "strconv"
  18. "time"
  19. "github.com/beego/beego/v2/server/web"
  20. )
  21. type OssController struct {
  22. web.Controller
  23. }
  24. // S3配置信息
  25. // S3配置信息
  26. var (
  27. accessKeyId string
  28. accessKeySecret string
  29. bucket string
  30. endpoint string
  31. region string = "raoxi" // S3区域,可根据实际情况调整
  32. host string
  33. )
  34. func init() {
  35. config := utils.GetOSSConfig()
  36. accessKeyId = config["access_key"]
  37. accessKeySecret = config["secret_key"]
  38. bucket = config["bucket"]
  39. endpoint = config["endpoint"]
  40. // 移除endpoint中的http://或https://前缀用于host拼接(如果需要)
  41. // 但原代码是: var host string = "http://" + endpoint + "/" + bucket
  42. // utils.GetOSSConfig返回的endpoint可能带http
  43. // 这里做个简单处理
  44. cleanEndpoint := endpoint
  45. if len(cleanEndpoint) > 7 && cleanEndpoint[:7] == "http://" {
  46. cleanEndpoint = cleanEndpoint[7:]
  47. } else if len(cleanEndpoint) > 8 && cleanEndpoint[:8] == "https://" {
  48. cleanEndpoint = cleanEndpoint[8:]
  49. }
  50. host = "http://" + cleanEndpoint + "/" + bucket
  51. }
  52. // S3服务地址 - 标准S3格式
  53. // var host string = "http://" + endpoint + "/" + bucket // 已在init中初始化
  54. // 用户上传文件时指定的前缀
  55. var upload_dir string = "uploads/"
  56. var expire_time int64 = 1800 // 30分钟
  57. // S3策略文档结构
  58. type S3PolicyDocument struct {
  59. Expiration string `json:"expiration"`
  60. Conditions []interface{} `json:"conditions"`
  61. }
  62. // S3响应结构
  63. type S3PolicyToken struct {
  64. URL string `json:"url"`
  65. Fields map[string]string `json:"fields"`
  66. Expire int64 `json:"expire"`
  67. StatusCode int `json:"statusCode"`
  68. }
  69. // 生成AWS4签名
  70. func generateAWS4Signature(secretKey, dateStamp, region, stringToSign string) string {
  71. kDate := hmacSHA256([]byte("AWS4"+secretKey), dateStamp)
  72. kRegion := hmacSHA256(kDate, region)
  73. kService := hmacSHA256(kRegion, "s3")
  74. kSigning := hmacSHA256(kService, "aws4_request")
  75. signature := hmacSHA256(kSigning, stringToSign)
  76. return hex.EncodeToString(signature)
  77. }
  78. // HMAC-SHA256计算
  79. func hmacSHA256(key []byte, data string) []byte {
  80. mac := hmac.New(sha256.New, key)
  81. mac.Write([]byte(data))
  82. return mac.Sum(nil)
  83. }
  84. func (c *OssController) Upload() {
  85. // 设置CORS头
  86. c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*")
  87. c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
  88. c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "Content-Type")
  89. // 处理OPTIONS预检请求
  90. if c.Ctx.Request.Method == "OPTIONS" {
  91. c.Ctx.ResponseWriter.WriteHeader(200)
  92. return
  93. }
  94. // 从token中获取用户信息
  95. userInfo, err := utils.GetUserInfoFromContext(c.Ctx.Input.GetData("userInfo"))
  96. if err != nil {
  97. c.Data["json"] = map[string]interface{}{
  98. "statusCode": 401,
  99. "error": "获取用户信息失败: " + err.Error(),
  100. }
  101. c.ServeJSON()
  102. return
  103. }
  104. user_id := int(userInfo.ID)
  105. if user_id == 0 {
  106. user_id = 1
  107. }
  108. // 生成时间相关字符串
  109. now := time.Now().UTC()
  110. expire_end := now.Unix() + expire_time
  111. dateStamp := now.Format("20060102")
  112. amzDate := now.Format("20060102T150405Z")
  113. expiration := now.Add(time.Duration(expire_time) * time.Second).Format("2006-01-02T15:04:05.000Z")
  114. // 生成credential
  115. credential := fmt.Sprintf("%s/%s/%s/s3/aws4_request", accessKeyId, dateStamp, region)
  116. // 生成上传目录
  117. uploadDir := upload_dir + now.Format("01") + now.Format("02") + "/" + strconv.Itoa(user_id) + "/"
  118. // 创建S3策略文档
  119. policy := S3PolicyDocument{
  120. Expiration: expiration,
  121. Conditions: []interface{}{
  122. map[string]string{"bucket": bucket},
  123. []interface{}{"starts-with", "$key", uploadDir},
  124. map[string]string{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
  125. map[string]string{"x-amz-credential": credential},
  126. map[string]string{"x-amz-date": amzDate},
  127. []interface{}{"content-length-range", "0", "104857600"}, // 最大100MB
  128. },
  129. }
  130. // 将策略文档转换为JSON并进行Base64编码
  131. policyJSON, err := json.Marshal(policy)
  132. if err != nil {
  133. c.Data["json"] = map[string]interface{}{
  134. "statusCode": 500,
  135. "error": "Failed to create policy",
  136. }
  137. c.ServeJSON()
  138. return
  139. }
  140. policyBase64 := base64.StdEncoding.EncodeToString(policyJSON)
  141. // 生成AWS4签名
  142. signature := generateAWS4Signature(accessKeySecret, dateStamp, region, policyBase64)
  143. // 构建表单字段
  144. fields := map[string]string{
  145. "key": uploadDir + "${filename}",
  146. "policy": policyBase64,
  147. "x-amz-algorithm": "AWS4-HMAC-SHA256",
  148. "x-amz-credential": credential,
  149. "x-amz-date": amzDate,
  150. "x-amz-signature": signature,
  151. }
  152. // 构建响应
  153. var policyToken S3PolicyToken
  154. policyToken.StatusCode = 200
  155. policyToken.URL = host
  156. policyToken.Fields = fields
  157. policyToken.Expire = expire_end
  158. c.Data["json"] = policyToken
  159. c.ServeJSON()
  160. }