|
|
@@ -2,6 +2,10 @@ package controllers
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
+ "crypto/hmac"
|
|
|
+ "crypto/sha256"
|
|
|
+ "encoding/base64"
|
|
|
+ "encoding/hex"
|
|
|
"encoding/json"
|
|
|
"fmt"
|
|
|
"image"
|
|
|
@@ -26,23 +30,27 @@ type ShudaoOssController struct {
|
|
|
web.Controller
|
|
|
}
|
|
|
|
|
|
-// OSS配置信息 - 从配置文件读取
|
|
|
-// 注意: 这些变量在init()函数中初始化
|
|
|
+// OSS配置信息 - 延迟初始化
|
|
|
var (
|
|
|
ossBucket string
|
|
|
ossAccessKey string
|
|
|
ossSecretKey string
|
|
|
ossEndpoint string
|
|
|
- ossRegion = "us-east-1" // 固定值
|
|
|
+ ossRegion = "us-east-1"
|
|
|
+ ossInited = false
|
|
|
)
|
|
|
|
|
|
-// init 初始化OSS配置
|
|
|
-func init() {
|
|
|
+// initOSSConfig 延迟初始化OSS配置
|
|
|
+func initOSSConfig() {
|
|
|
+ if ossInited {
|
|
|
+ return
|
|
|
+ }
|
|
|
ossConfig := utils.GetOSSConfig()
|
|
|
ossBucket = ossConfig["bucket"]
|
|
|
ossAccessKey = ossConfig["access_key"]
|
|
|
ossSecretKey = ossConfig["secret_key"]
|
|
|
ossEndpoint = ossConfig["endpoint"]
|
|
|
+ ossInited = true
|
|
|
}
|
|
|
|
|
|
// 图片压缩配置
|
|
|
@@ -68,60 +76,33 @@ type UploadResponse struct {
|
|
|
FileSize int64 `json:"fileSize"`
|
|
|
}
|
|
|
|
|
|
-// 获取S3会话
|
|
|
+// getS3Session 获取S3会话
|
|
|
func getS3Session() (*session.Session, error) {
|
|
|
- // 检查时间同步
|
|
|
- now := time.Now()
|
|
|
- utcNow := now.UTC()
|
|
|
- fmt.Printf("=== S3会话创建调试信息 ===\n")
|
|
|
- fmt.Printf("本地时间: %s\n", now.Format("2006-01-02 15:04:05"))
|
|
|
- fmt.Printf("UTC时间: %s\n", utcNow.Format("2006-01-02 15:04:05"))
|
|
|
- fmt.Printf("时间差: %v\n", now.Sub(utcNow))
|
|
|
- fmt.Printf("Bucket: %s\n", ossBucket)
|
|
|
- fmt.Printf("Endpoint: %s\n", ossEndpoint)
|
|
|
- fmt.Printf("Region: %s\n", ossRegion)
|
|
|
- fmt.Printf("AccessKey: %s***\n", ossAccessKey[:8])
|
|
|
-
|
|
|
+ initOSSConfig()
|
|
|
s3Config := &aws.Config{
|
|
|
Credentials: credentials.NewStaticCredentials(ossAccessKey, ossSecretKey, ""),
|
|
|
Endpoint: aws.String(ossEndpoint),
|
|
|
Region: aws.String(ossRegion),
|
|
|
- S3ForcePathStyle: aws.Bool(true), // 使用路径样式而不是虚拟主机样式
|
|
|
- DisableSSL: aws.Bool(true), // 如果endpoint是http则禁用SSL
|
|
|
- // 添加更多配置选项
|
|
|
- LogLevel: aws.LogLevel(aws.LogDebug), // 启用调试日志
|
|
|
- // 其他配置选项
|
|
|
- MaxRetries: aws.Int(3),
|
|
|
- // 其他配置选项
|
|
|
+ S3ForcePathStyle: aws.Bool(true),
|
|
|
+ DisableSSL: aws.Bool(true),
|
|
|
+ MaxRetries: aws.Int(3),
|
|
|
}
|
|
|
|
|
|
- // 创建会话
|
|
|
sess, err := session.NewSession(s3Config)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- // 验证凭据
|
|
|
- _, err = sess.Config.Credentials.Get()
|
|
|
- if err != nil {
|
|
|
+ if _, err = sess.Config.Credentials.Get(); err != nil {
|
|
|
return nil, fmt.Errorf("凭据验证失败: %v", err)
|
|
|
}
|
|
|
|
|
|
return sess, nil
|
|
|
}
|
|
|
|
|
|
-// 获取UTC时间同步的S3会话(根据测试文件优化配置)
|
|
|
+// getUTCS3Session 获取UTC时间同步的S3会话
|
|
|
func getUTCS3Session() (*session.Session, error) {
|
|
|
- // 强制使用UTC时间
|
|
|
- utcNow := time.Now().UTC()
|
|
|
- fmt.Printf("=== UTC S3会话创建调试信息 ===\n")
|
|
|
- fmt.Printf("强制使用UTC时间: %s\n", utcNow.Format("2006-01-02 15:04:05"))
|
|
|
- fmt.Printf("Bucket: %s\n", ossBucket)
|
|
|
- fmt.Printf("Endpoint: %s\n", ossEndpoint)
|
|
|
- fmt.Printf("Region: %s\n", ossRegion)
|
|
|
- fmt.Printf("AccessKey: %s***\n", ossAccessKey[:8])
|
|
|
-
|
|
|
- // 创建S3配置 - 使用与测试文件相同的配置
|
|
|
+ initOSSConfig()
|
|
|
s3Config := &aws.Config{
|
|
|
Credentials: credentials.NewStaticCredentials(ossAccessKey, ossSecretKey, ""),
|
|
|
Endpoint: aws.String(ossEndpoint),
|
|
|
@@ -230,17 +211,10 @@ func (c *ShudaoOssController) UploadImage() {
|
|
|
|
|
|
// 压缩图片
|
|
|
if EnableImageCompression {
|
|
|
- fmt.Printf("开始压缩图片到200KB以下...\n")
|
|
|
- compressedBytes, err := compressImage(fileBytes, MaxImageWidth, MaxImageHeight, 0) // 参数不再使用
|
|
|
- if err != nil {
|
|
|
- fmt.Printf("图片压缩失败,使用原始图片: %v\n", err)
|
|
|
- // 压缩失败时使用原始图片
|
|
|
- } else {
|
|
|
- fmt.Printf("图片压缩完成,最终大小: %.2f KB\n", float64(len(compressedBytes))/1024)
|
|
|
+ compressedBytes, err := compressImage(fileBytes, MaxImageWidth, MaxImageHeight, 0)
|
|
|
+ if err == nil {
|
|
|
fileBytes = compressedBytes
|
|
|
}
|
|
|
- } else {
|
|
|
- fmt.Printf("图片压缩已禁用,使用原始图片\n")
|
|
|
}
|
|
|
|
|
|
// 获取UTC时间同步的S3会话(解决时区问题)
|
|
|
@@ -257,12 +231,7 @@ func (c *ShudaoOssController) UploadImage() {
|
|
|
// 创建S3服务
|
|
|
s3Client := s3.New(sess)
|
|
|
|
|
|
- // 打印上传信息
|
|
|
- fmt.Printf("正在上传图片: %s\n", fileName)
|
|
|
- fmt.Printf("文件大小: %.2f KB\n", float64(len(fileBytes))/1024)
|
|
|
- fmt.Printf("ContentType: %s\n", header.Header.Get("Content-Type"))
|
|
|
-
|
|
|
- // 上传图片到S3 - 使用与测试文件相同的上传方式
|
|
|
+ // 上传图片到S3
|
|
|
_, err = s3Client.PutObject(&s3.PutObjectInput{
|
|
|
Bucket: aws.String(ossBucket),
|
|
|
Key: aws.String(fileName),
|
|
|
@@ -271,7 +240,6 @@ func (c *ShudaoOssController) UploadImage() {
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
- fmt.Printf("上传图片失败: %v\n", err)
|
|
|
c.Data["json"] = UploadResponse{
|
|
|
StatusCode: 500,
|
|
|
Message: "上传图片到OSS失败: " + err.Error(),
|
|
|
@@ -302,11 +270,7 @@ func (c *ShudaoOssController) UploadImage() {
|
|
|
// return
|
|
|
// }
|
|
|
permanentURL := fmt.Sprintf("%s/%s/%s", ossEndpoint, ossBucket, fileName)
|
|
|
- // 使用代理接口包装URL,前端需要预览图片
|
|
|
proxyURL := utils.GetProxyURL(permanentURL)
|
|
|
- fmt.Printf("图片上传成功: %s\n", fileName)
|
|
|
- fmt.Printf("文件URL: %s\n", permanentURL)
|
|
|
- fmt.Printf("代理URL: %s\n", proxyURL)
|
|
|
|
|
|
c.Data["json"] = UploadResponse{
|
|
|
StatusCode: 200,
|
|
|
@@ -407,11 +371,6 @@ func (c *ShudaoOssController) UploadPPTJson() {
|
|
|
// 创建S3服务
|
|
|
s3Client := s3.New(sess)
|
|
|
|
|
|
- // 打印上传信息
|
|
|
- fmt.Printf("正在上传JSON文件: %s\n", fileName)
|
|
|
- fmt.Printf("文件大小: %.2f KB\n", float64(len(fileBytes))/1024)
|
|
|
- fmt.Printf("ContentType: %s\n", header.Header.Get("Content-Type"))
|
|
|
-
|
|
|
// 上传JSON到S3
|
|
|
_, err = s3Client.PutObject(&s3.PutObjectInput{
|
|
|
Bucket: aws.String(ossBucket),
|
|
|
@@ -422,7 +381,6 @@ func (c *ShudaoOssController) UploadPPTJson() {
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
- fmt.Printf("上传JSON文件失败: %v\n", err)
|
|
|
c.Data["json"] = UploadResponse{
|
|
|
StatusCode: 500,
|
|
|
Message: "上传JSON文件到OSS失败: " + err.Error(),
|
|
|
@@ -433,11 +391,7 @@ func (c *ShudaoOssController) UploadPPTJson() {
|
|
|
|
|
|
// 生成永久URL
|
|
|
permanentURL := fmt.Sprintf("%s/%s/%s", ossEndpoint, ossBucket, fileName)
|
|
|
- // 使用代理接口包装URL,前端需要访问文件
|
|
|
proxyURL := utils.GetProxyURL(permanentURL)
|
|
|
- fmt.Printf("JSON文件上传成功: %s\n", fileName)
|
|
|
- fmt.Printf("文件URL: %s\n", permanentURL)
|
|
|
- fmt.Printf("代理URL: %s\n", proxyURL)
|
|
|
|
|
|
c.Data["json"] = UploadResponse{
|
|
|
StatusCode: 200,
|
|
|
@@ -473,7 +427,6 @@ func (c *ShudaoOssController) ParseOSS() {
|
|
|
// 解密URL
|
|
|
decryptedURL, err := utils.DecryptURL(encryptedURL)
|
|
|
if err != nil {
|
|
|
- fmt.Printf("URL解密失败: %v, 加密URL: %s\n", err, encryptedURL)
|
|
|
c.Ctx.ResponseWriter.WriteHeader(400)
|
|
|
c.Ctx.WriteString("URL解密失败: " + err.Error())
|
|
|
return
|
|
|
@@ -482,8 +435,6 @@ func (c *ShudaoOssController) ParseOSS() {
|
|
|
// URL解码,处理可能的编码问题
|
|
|
decodedURL, err := neturl.QueryUnescape(decryptedURL)
|
|
|
if err != nil {
|
|
|
- fmt.Printf("URL解码失败: %v, 解密后URL: %s\n", err, decryptedURL)
|
|
|
- // 如果解码失败,使用解密后的URL
|
|
|
decodedURL = decryptedURL
|
|
|
}
|
|
|
|
|
|
@@ -491,48 +442,37 @@ func (c *ShudaoOssController) ParseOSS() {
|
|
|
|
|
|
// 检查是否是代理URL格式(包含?url=参数)
|
|
|
if strings.Contains(decodedURL, "?url=") {
|
|
|
- // 解析代理URL,提取实际的OSS URL
|
|
|
parsedProxyURL, err := neturl.Parse(decodedURL)
|
|
|
if err != nil {
|
|
|
- fmt.Printf("代理URL解析失败: %v, 解码后URL: %s\n", err, decodedURL)
|
|
|
c.Ctx.ResponseWriter.WriteHeader(400)
|
|
|
c.Ctx.WriteString("代理URL格式无效: " + err.Error())
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- // 获取实际的OSS URL
|
|
|
actualOSSURL = parsedProxyURL.Query().Get("url")
|
|
|
if actualOSSURL == "" {
|
|
|
- fmt.Printf("代理URL中缺少url参数: %s\n", decodedURL)
|
|
|
c.Ctx.ResponseWriter.WriteHeader(400)
|
|
|
c.Ctx.WriteString("代理URL中缺少url参数")
|
|
|
return
|
|
|
}
|
|
|
} else {
|
|
|
- // 直接使用传入的URL作为OSS URL
|
|
|
actualOSSURL = decodedURL
|
|
|
- fmt.Printf("检测到直接OSS URL: %s\n", actualOSSURL)
|
|
|
}
|
|
|
|
|
|
// 验证实际OSS URL格式
|
|
|
parsedOSSURL, err := neturl.Parse(actualOSSURL)
|
|
|
if err != nil {
|
|
|
- fmt.Printf("OSS URL解析失败: %v, OSS URL: %s\n", err, actualOSSURL)
|
|
|
c.Ctx.ResponseWriter.WriteHeader(400)
|
|
|
c.Ctx.WriteString("OSS URL格式无效: " + err.Error())
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if parsedOSSURL.Scheme == "" {
|
|
|
- fmt.Printf("OSS URL缺少协议方案: %s\n", actualOSSURL)
|
|
|
c.Ctx.ResponseWriter.WriteHeader(400)
|
|
|
c.Ctx.WriteString("OSS URL缺少协议方案")
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- fmt.Printf("代理请求 - 加密URL: %s, 解密后URL: %s, 解码后URL: %s, 实际OSS URL: %s, 协议: %s\n",
|
|
|
- encryptedURL, decryptedURL, decodedURL, actualOSSURL, parsedOSSURL.Scheme)
|
|
|
-
|
|
|
// 创建HTTP客户端,设置超时时间
|
|
|
client := &http.Client{
|
|
|
Timeout: 30 * time.Second,
|
|
|
@@ -622,42 +562,17 @@ func (c *ShudaoOssController) ParseOSS() {
|
|
|
|
|
|
// compressImage 压缩图片到目标大小
|
|
|
func compressImage(imageData []byte, maxWidth, maxHeight int, quality int) ([]byte, error) {
|
|
|
- // 解码图片
|
|
|
- img, format, err := image.Decode(bytes.NewReader(imageData))
|
|
|
+ img, _, err := image.Decode(bytes.NewReader(imageData))
|
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("解码图片失败: %v", err)
|
|
|
}
|
|
|
|
|
|
- // 获取原始尺寸
|
|
|
- bounds := img.Bounds()
|
|
|
- originalWidth := bounds.Dx()
|
|
|
- originalHeight := bounds.Dy()
|
|
|
originalSize := len(imageData)
|
|
|
-
|
|
|
- fmt.Printf("原始图片: %dx%d, 格式: %s, 大小: %.2f KB\n",
|
|
|
- originalWidth, originalHeight, format, float64(originalSize)/1024)
|
|
|
-
|
|
|
- // 如果原始文件已经小于目标大小,直接返回
|
|
|
if originalSize <= TargetFileSize {
|
|
|
- fmt.Printf("文件已小于目标大小,无需压缩\n")
|
|
|
return imageData, nil
|
|
|
}
|
|
|
|
|
|
- // 尝试不同的压缩策略
|
|
|
- compressedData, err := compressToTargetSize(img, originalSize)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- finalSize := len(compressedData)
|
|
|
- compressionRatio := float64(finalSize) / float64(originalSize) * 100
|
|
|
-
|
|
|
- fmt.Printf("压缩完成: %.2f KB -> %.2f KB (压缩率: %.1f%%)\n",
|
|
|
- float64(originalSize)/1024,
|
|
|
- float64(finalSize)/1024,
|
|
|
- compressionRatio)
|
|
|
-
|
|
|
- return compressedData, nil
|
|
|
+ return compressToTargetSize(img, originalSize)
|
|
|
}
|
|
|
|
|
|
// compressToTargetSize 压缩到目标文件大小
|
|
|
@@ -667,24 +582,18 @@ func compressToTargetSize(img image.Image, originalSize int) ([]byte, error) {
|
|
|
originalHeight := bounds.Dy()
|
|
|
|
|
|
// 策略1: 先尝试调整质量,不改变尺寸
|
|
|
- fmt.Printf("策略1: 调整质量压缩...\n")
|
|
|
- compressedData, err := compressByQuality(img, originalSize)
|
|
|
+ compressedData, err := compressByQuality(img)
|
|
|
if err == nil && len(compressedData) <= TargetFileSize {
|
|
|
- fmt.Printf("质量压缩成功,达到目标大小\n")
|
|
|
return compressedData, nil
|
|
|
}
|
|
|
|
|
|
// 策略2: 如果质量压缩不够,尝试缩小尺寸
|
|
|
- fmt.Printf("策略2: 尺寸+质量压缩...\n")
|
|
|
-
|
|
|
- // 计算需要缩小的比例
|
|
|
targetRatio := float64(TargetFileSize) / float64(originalSize)
|
|
|
- sizeRatio := math.Sqrt(targetRatio * 0.8) // 留一些余量给质量调整
|
|
|
+ sizeRatio := math.Sqrt(targetRatio * 0.8)
|
|
|
|
|
|
newWidth := int(float64(originalWidth) * sizeRatio)
|
|
|
newHeight := int(float64(originalHeight) * sizeRatio)
|
|
|
|
|
|
- // 确保最小尺寸
|
|
|
if newWidth < 100 {
|
|
|
newWidth = 100
|
|
|
}
|
|
|
@@ -692,21 +601,15 @@ func compressToTargetSize(img image.Image, originalSize int) ([]byte, error) {
|
|
|
newHeight = 100
|
|
|
}
|
|
|
|
|
|
- fmt.Printf("调整尺寸: %dx%d -> %dx%d\n", originalWidth, originalHeight, newWidth, newHeight)
|
|
|
-
|
|
|
- // 调整图片尺寸
|
|
|
resizedImg := resizeImage(img, newWidth, newHeight)
|
|
|
-
|
|
|
- // 再次尝试质量压缩
|
|
|
- return compressByQuality(resizedImg, originalSize)
|
|
|
+ return compressByQuality(resizedImg)
|
|
|
}
|
|
|
|
|
|
// compressByQuality 通过调整质量压缩图片
|
|
|
-func compressByQuality(img image.Image, originalSize int) ([]byte, error) {
|
|
|
+func compressByQuality(img image.Image) ([]byte, error) {
|
|
|
var bestResult []byte
|
|
|
- var bestSize int = originalSize
|
|
|
+ var bestSize int = math.MaxInt32
|
|
|
|
|
|
- // 从高质量到低质量尝试
|
|
|
qualities := []int{85, 70, 60, 50, 40, 30, 25, 20, 15, 10}
|
|
|
|
|
|
for _, quality := range qualities {
|
|
|
@@ -716,24 +619,17 @@ func compressByQuality(img image.Image, originalSize int) ([]byte, error) {
|
|
|
}
|
|
|
|
|
|
currentSize := buf.Len()
|
|
|
- fmt.Printf(" 质量 %d: %.2f KB\n", quality, float64(currentSize)/1024)
|
|
|
-
|
|
|
- // 如果达到目标大小,直接返回
|
|
|
if currentSize <= TargetFileSize {
|
|
|
- fmt.Printf(" 达到目标大小,质量: %d\n", quality)
|
|
|
return buf.Bytes(), nil
|
|
|
}
|
|
|
|
|
|
- // 记录最佳结果
|
|
|
if currentSize < bestSize {
|
|
|
bestSize = currentSize
|
|
|
bestResult = buf.Bytes()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 如果没有达到目标大小,返回最佳结果
|
|
|
if bestResult != nil {
|
|
|
- fmt.Printf(" 未达到目标大小,使用最佳结果: %.2f KB\n", float64(bestSize)/1024)
|
|
|
return bestResult, nil
|
|
|
}
|
|
|
|
|
|
@@ -767,3 +663,99 @@ func resizeImage(img image.Image, newWidth, newHeight int) image.Image {
|
|
|
|
|
|
return resized
|
|
|
}
|
|
|
+
|
|
|
+// 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"`
|
|
|
+}
|
|
|
+
|
|
|
+// Upload 生成S3预签名上传凭证
|
|
|
+func (c *ShudaoOssController) Upload() {
|
|
|
+ initOSSConfig()
|
|
|
+ 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")
|
|
|
+
|
|
|
+ if c.Ctx.Request.Method == "OPTIONS" {
|
|
|
+ c.Ctx.ResponseWriter.WriteHeader(200)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ userInfo, err := utils.GetUserInfoFromContext(c.Ctx.Input.GetData("userInfo"))
|
|
|
+ if err != nil {
|
|
|
+ c.Data["json"] = map[string]interface{}{"statusCode": 401, "error": "获取用户信息失败"}
|
|
|
+ c.ServeJSON()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ userID := int(userInfo.ID)
|
|
|
+ if userID == 0 {
|
|
|
+ userID = 1
|
|
|
+ }
|
|
|
+
|
|
|
+ now := time.Now().UTC()
|
|
|
+ expireTime := int64(1800)
|
|
|
+ expireEnd := now.Unix() + expireTime
|
|
|
+ dateStamp := now.Format("20060102")
|
|
|
+ amzDate := now.Format("20060102T150405Z")
|
|
|
+ expiration := now.Add(time.Duration(expireTime) * time.Second).Format("2006-01-02T15:04:05.000Z")
|
|
|
+
|
|
|
+ credential := fmt.Sprintf("%s/%s/%s/s3/aws4_request", ossAccessKey, dateStamp, ossRegion)
|
|
|
+ uploadDir := fmt.Sprintf("uploads/%s/%d/", now.Format("0102"), userID)
|
|
|
+ host := fmt.Sprintf("%s/%s", ossEndpoint, ossBucket)
|
|
|
+
|
|
|
+ policy := S3PolicyDocument{
|
|
|
+ Expiration: expiration,
|
|
|
+ Conditions: []interface{}{
|
|
|
+ map[string]string{"bucket": ossBucket},
|
|
|
+ []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"},
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ policyJSON, _ := json.Marshal(policy)
|
|
|
+ policyBase64 := base64.StdEncoding.EncodeToString(policyJSON)
|
|
|
+ signature := generateAWS4Signature(ossSecretKey, dateStamp, ossRegion, policyBase64)
|
|
|
+
|
|
|
+ c.Data["json"] = S3PolicyToken{
|
|
|
+ StatusCode: 200,
|
|
|
+ URL: host,
|
|
|
+ Expire: expireEnd,
|
|
|
+ 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,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ c.ServeJSON()
|
|
|
+}
|
|
|
+
|
|
|
+// generateAWS4Signature 生成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")
|
|
|
+ return hex.EncodeToString(hmacSHA256(kSigning, stringToSign))
|
|
|
+}
|
|
|
+
|
|
|
+// hmacSHA256 HMAC-SHA256计算
|
|
|
+func hmacSHA256(key []byte, data string) []byte {
|
|
|
+ mac := hmac.New(sha256.New, key)
|
|
|
+ mac.Write([]byte(data))
|
|
|
+ return mac.Sum(nil)
|
|
|
+}
|