From e62bdbb9f12f4ab66fbd115a0c5a6df87843de82 Mon Sep 17 00:00:00 2001 From: tickstep studio team Date: Mon, 29 Jun 2026 10:27:00 +0800 Subject: [PATCH] refractor OSS url parser --- internal/file/downloader/utils.go | 23 ++-------- internal/functions/panupload/utils.go | 16 +++---- internal/utils/utils.go | 61 +++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/internal/file/downloader/utils.go b/internal/file/downloader/utils.go index e2add24..0c168af 100644 --- a/internal/file/downloader/utils.go +++ b/internal/file/downloader/utils.go @@ -23,6 +23,7 @@ import ( "strconv" "time" + "github.com/tickstep/aliyunpan/internal/utils" "github.com/tickstep/library-go/logger" "github.com/tickstep/library-go/requester" ) @@ -103,31 +104,15 @@ func fixCacheSize(size *int) { // IsUrlExpired 下载链接是否已过期。过期返回True func IsUrlExpired(urlStr string) bool { - u, err := url.Parse(urlStr) + expiredTimeSec, err := utils.GetExpiredTimeSecFromOSSUrl(urlStr) if err != nil { + // 解析错误,默认过期 return true } - // OSS V4签名:x-oss-date=20260629T010316Z&x-oss-expires=900 - expiredDateStr := u.Query().Get("x-oss-date") // 该请求/签名生成的时间 - expiredTimeSecStr := u.Query().Get("x-oss-expires") // Presigned URL 的过期时间(单位:秒) - - // 解析 x-oss-date(ISO8601,UTC) - expiredDate, err := time.Parse("20060102T150405Z", expiredDateStr) - if err != nil { - // 非法时间格式 - return true - } - // 解析 x-oss-expires(秒) - expireSec, err := strconv.ParseInt(expiredTimeSecStr, 10, 64) - if err != nil || expireSec <= 0 { - // 非法过期时间 - return true - } - // 计算过期时间戳(Unix seconds) - expiredTimeSec := expiredDate.Unix() + expireSec if (expiredTimeSec - time.Now().Unix()) <= 5 { // 小于5秒钟 // expired return true } + // 返回未过期 return false } diff --git a/internal/functions/panupload/utils.go b/internal/functions/panupload/utils.go index 1171ace..354db39 100644 --- a/internal/functions/panupload/utils.go +++ b/internal/functions/panupload/utils.go @@ -16,15 +16,15 @@ package panupload import ( "crypto/sha1" "encoding/hex" - "github.com/tickstep/aliyunpan/internal/config" - "github.com/tickstep/library-go/converter" - "github.com/tickstep/library-go/logger" - "net/url" "os" "path" - "strconv" "strings" "time" + + "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/aliyunpan/internal/utils" + "github.com/tickstep/library-go/converter" + "github.com/tickstep/library-go/logger" ) const ( @@ -55,16 +55,16 @@ func getBlockSize(fileSize int64) int64 { // IsUrlExpired 上传链接是否已过期。过期返回True func IsUrlExpired(urlStr string) bool { - u, err := url.Parse(urlStr) + expiredTimeSec, err := utils.GetExpiredTimeSecFromOSSUrl(urlStr) if err != nil { + // 解析错误,默认过期 return true } - expiredTimeSecStr := u.Query().Get("x-oss-expires") - expiredTimeSec, _ := strconv.ParseInt(expiredTimeSecStr, 10, 64) if (expiredTimeSec - time.Now().Unix()) <= 300 { // 小于5分钟 // expired return true } + // 返回未过期 return false } diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 31d0504..405bba6 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -383,3 +383,64 @@ func GetPublicIP() (string, error) { ip := strings.TrimSpace(strings.Split(ipResp.Origin, ",")[0]) return ip, nil } + +// GetExpiredTimeSecFromOSSUrl 从OSS URL中解析出统一的过期时间戳(秒) +func GetExpiredTimeSecFromOSSUrl(rawURL string) (int64, error) { + parsedURL, err := url.Parse(rawURL) + if err != nil { + return 0, fmt.Errorf("failed to parse URL: %w", err) + } + + query := parsedURL.Query() + sigVersion := query.Get("x-oss-signature-version") + + var expires int64 = 0 + + // 检测版本标识(不区分大小写) + if strings.Contains(sigVersion, "OSS4") { + // V4 签名:x-oss-expires 为相对秒数,需要结合 x-oss-date + expiresStr := query.Get("x-oss-expires") + if expiresStr == "" { + return 0, fmt.Errorf("V4 signature: 'x-oss-expires' parameter not found") + } + relSec, err := strconv.ParseInt(expiresStr, 10, 64) + if err != nil { + return 0, fmt.Errorf("V4 signature: invalid 'x-oss-expires' value: %w", err) + } + + dateStr := query.Get("x-oss-date") + if dateStr == "" { + return 0, fmt.Errorf("V4 signature: 'x-oss-date' parameter not found") + } + // 解析 ISO8601 UTC 时间,例如 "20231220T083018Z" + t, err := time.Parse("20060102T150405Z", dateStr) + if err != nil { + return 0, fmt.Errorf("V4 signature: failed to parse 'x-oss-date': %w", err) + } + expires = t.Unix() + relSec + + } else if strings.Contains(sigVersion, "OSS3") || strings.Contains(sigVersion, "OSS2") { + // V2 / V3:x-oss-expires 为绝对时间戳 + expiresStr := query.Get("x-oss-expires") + if expiresStr == "" { + return 0, fmt.Errorf("V2/V3 signature: 'x-oss-expires' parameter not found") + } + expires, err = strconv.ParseInt(expiresStr, 10, 64) + if err != nil { + return 0, fmt.Errorf("V2/V3 signature: invalid 'x-oss-expires' value: %w", err) + } + + } else { + // 默认为 V1 签名(没有 x-oss-signature-version 或为空) + expiresStr := query.Get("Expires") + if expiresStr == "" { + return 0, fmt.Errorf("V1 signature: 'Expires' parameter not found") + } + expires, err = strconv.ParseInt(expiresStr, 10, 64) + if err != nil { + return 0, fmt.Errorf("V1 signature: invalid 'Expires' value: %w", err) + } + } + + return expires, nil +}