From 6bb4c8800a9c27f9139e4a6e3e395931826f310f Mon Sep 17 00:00:00 2001 From: Mingluan Mu Date: Sat, 10 Oct 2026 11:44:49 +0800 Subject: [PATCH] fix(drivers/s3): sign content-type for direct uploads (#3152) * fix(s3): sign content type for direct uploads - Include the inferred MIME type in presigned PutObject requests - Return the signed Content-Type header for frontend uploads Co-authored-by: Codex <267193182+codex@users.noreply.github.com> * fix(drivers/s3): use client content type for direct uploads - Accept and validate the client-provided direct upload media type - Pass the type to the S3 signer through a typed context key - Default missing types to application/octet-stream Co-authored-by: Codex <267193182+codex@users.noreply.github.com> --------- Co-authored-by: Codex <267193182+codex@users.noreply.github.com> --- drivers/s3/driver.go | 11 +++++++++-- internal/conf/const.go | 1 + server/handles/direct_upload.go | 20 +++++++++++++++----- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/s3/driver.go b/drivers/s3/driver.go index 711f46ab5..253d3c0a3 100644 --- a/drivers/s3/driver.go +++ b/drivers/s3/driver.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/driver" "github.com/OpenListTeam/OpenList/v4/internal/errs" "github.com/OpenListTeam/OpenList/v4/internal/model" @@ -229,9 +230,14 @@ func (d *S3) GetDirectUploadInfo(ctx context.Context, _ string, dstDir model.Obj return nil, errs.NotImplement } path := getKey(stdpath.Join(dstDir.GetPath(), fileName), false) + contentType, _ := ctx.Value(conf.DirectUploadContentTypeKey).(string) + if contentType == "" { + contentType = "application/octet-stream" + } req, _ := d.directUploadClient.PutObjectRequest(&s3.PutObjectInput{ - Bucket: &d.Bucket, - Key: &path, + Bucket: &d.Bucket, + Key: &path, + ContentType: &contentType, }) if req == nil { return nil, fmt.Errorf("failed to create PutObject request") @@ -243,6 +249,7 @@ func (d *S3) GetDirectUploadInfo(ctx context.Context, _ string, dstDir model.Obj return &model.HttpDirectUploadInfo{ UploadURL: link, Method: "PUT", + Headers: map[string]string{"Content-Type": contentType}, }, nil } diff --git a/internal/conf/const.go b/internal/conf/const.go index 2193d3d9d..9a0f11a44 100644 --- a/internal/conf/const.go +++ b/internal/conf/const.go @@ -197,4 +197,5 @@ const ( PathKey SharingIDKey SkipHookKey + DirectUploadContentTypeKey ) diff --git a/server/handles/direct_upload.go b/server/handles/direct_upload.go index b016f028c..221b1afa4 100644 --- a/server/handles/direct_upload.go +++ b/server/handles/direct_upload.go @@ -1,6 +1,8 @@ package handles import ( + "context" + "mime" "net/url" stdpath "path" @@ -15,10 +17,11 @@ import ( ) type FsGetDirectUploadInfoReq struct { - Path string `json:"path" form:"path"` - FileName string `json:"file_name" form:"file_name"` - FileSize int64 `json:"file_size" form:"file_size"` - Tool string `json:"tool" form:"tool"` + Path string `json:"path" form:"path"` + FileName string `json:"file_name" form:"file_name"` + FileSize int64 `json:"file_size" form:"file_size"` + ContentType string `json:"content_type" form:"content_type"` + Tool string `json:"tool" form:"tool"` } // FsGetDirectUploadInfo returns the direct upload info if supported by the driver @@ -91,7 +94,14 @@ func FsGetDirectUploadInfo(c *gin.Context) { return } } - directUploadInfo, err := fs.GetDirectUploadInfo(c, req.Tool, path, req.FileName, req.FileSize, overwrite) + if req.ContentType != "" { + if _, _, err := mime.ParseMediaType(req.ContentType); err != nil { + common.ErrorResp(c, err, 400) + return + } + } + ctx := context.WithValue(c, conf.DirectUploadContentTypeKey, req.ContentType) + directUploadInfo, err := fs.GetDirectUploadInfo(ctx, req.Tool, path, req.FileName, req.FileSize, overwrite) if err != nil { if !overwrite && errs.IsObjectAlreadyExists(err) { common.ErrorStrResp(c, "file exists", 403)