fix(storage): COS 资源下载 - 公开 CDN 模式使用附件响应头 (#718)

This commit is contained in:
MasterChiefCN
2026-10-10 09:10:59 +08:00
committed by GitHub
parent c3dc8c8038
commit 37b44444f0
5 changed files with 88 additions and 7 deletions

View File

@@ -248,6 +248,28 @@ func SignedOriginObjectDownloadURL(setting Settings, objectKey string, expiresAt
return signedOriginObjectURL(setting, objectKey, objectURLSigning{ExpiresAt: expiresAt, ContentDisposition: disposition})
}
// PublicCDNObjectDownloadURL returns a public CDN URL that asks the object
// storage origin to emit an attachment response. The query parameter keeps
// ordinary display URLs inline while allowing a browser to download this one
// without routing object bytes through the application server.
//
// Public CDN deployments must preserve response-content-disposition when
// forwarding requests to the object store.
func PublicCDNObjectDownloadURL(setting Settings, objectKey string, fileName string) (string, error) {
value, err := OssCDNObjectURL(setting.CDNBaseURL, objectKey)
if err != nil {
return "", err
}
parsed, err := url.Parse(value)
if err != nil {
return "", err
}
query := parsed.Query()
query.Set("response-content-disposition", objectDownloadContentDisposition(fileName, objectKey))
parsed.RawQuery = query.Encode()
return parsed.String(), nil
}
func objectDownloadContentDisposition(fileName string, objectKey string) string {
name := strings.TrimSpace(fileName)
name = strings.ReplaceAll(name, "\\", "_")

View File

@@ -76,6 +76,28 @@ func TestOneShotURLsKeepCurrentTimeSigningWithoutCacheOverride(t *testing.T) {
}
}
func TestPublicCDNObjectDownloadURLForcesAttachmentOnlyOnDownloadURL(t *testing.T) {
setting := Settings{CDNBaseURL: "https://media.example.com"}
value, err := PublicCDNObjectDownloadURL(setting, "users/u-1/image/test image.png", "下载图片.png")
if err != nil {
t.Fatal(err)
}
parsed, err := url.Parse(value)
if err != nil {
t.Fatal(err)
}
if parsed.Host != "media.example.com" || parsed.Path != "/users/u-1/image/test image.png" || !strings.HasPrefix(parsed.Query().Get("response-content-disposition"), "attachment") {
t.Fatalf("download URL = %q", value)
}
inline, err := OssCDNObjectURL(setting.CDNBaseURL, "users/u-1/image/test image.png")
if err != nil {
t.Fatal(err)
}
if parsedInline, err := url.Parse(inline); err != nil || parsedInline.RawQuery != "" {
t.Fatalf("inline CDN URL = %q, %v", inline, err)
}
}
func mustParseQuery(t *testing.T, raw string) url.Values {
t.Helper()
parsed, err := url.Parse(raw)