Skip to content

Commit f54fa21

Browse files
authored
feat(#1755): GetObject supports overriding response header values (#1756)
1 parent 438fd0f commit f54fa21

7 files changed

+156
-13
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
*.test
33
validator
44
golangci-lint
5-
functional_tests
5+
functional_tests
6+
.idea

api-get-object.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ import (
2323
"fmt"
2424
"io"
2525
"net/http"
26-
"net/url"
27-
"strconv"
2826
"sync"
2927

3028
"github.com/minio/minio-go/v7/pkg/s3utils"
@@ -654,19 +652,11 @@ func (c *Client) getObject(ctx context.Context, bucketName, objectName string, o
654652
return nil, ObjectInfo{}, nil, err
655653
}
656654

657-
urlValues := make(url.Values)
658-
if opts.VersionID != "" {
659-
urlValues.Set("versionId", opts.VersionID)
660-
}
661-
if opts.PartNumber > 0 {
662-
urlValues.Set("partNumber", strconv.Itoa(opts.PartNumber))
663-
}
664-
665655
// Execute GET on objectName.
666656
resp, err := c.executeMethod(ctx, http.MethodGet, requestMetadata{
667657
bucketName: bucketName,
668658
objectName: objectName,
669-
queryValues: urlValues,
659+
queryValues: opts.toQueryValues(),
670660
customHeader: opts.Header(),
671661
contentSHA256Hex: emptySHA256Hex,
672662
})

api-get-options.go

+52
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ package minio
2020
import (
2121
"fmt"
2222
"net/http"
23+
"net/url"
24+
"strconv"
2325
"time"
2426

2527
"github.com/minio/minio-go/v7/pkg/encrypt"
@@ -36,6 +38,7 @@ type AdvancedGetOptions struct {
3638
// during GET requests.
3739
type GetObjectOptions struct {
3840
headers map[string]string
41+
reqParams url.Values
3942
ServerSideEncryption encrypt.ServerSide
4043
VersionID string
4144
PartNumber int
@@ -83,6 +86,34 @@ func (o *GetObjectOptions) Set(key, value string) {
8386
o.headers[http.CanonicalHeaderKey(key)] = value
8487
}
8588

89+
// SetReqParam - set request query string parameter
90+
// supported key: see supportedQueryValues.
91+
// If an unsupported key is passed in, it will be ignored and nothing will be done.
92+
func (o *GetObjectOptions) SetReqParam(key, value string) {
93+
if !isStandardQueryValue(key) {
94+
// do nothing
95+
return
96+
}
97+
if o.reqParams == nil {
98+
o.reqParams = make(url.Values)
99+
}
100+
o.reqParams.Set(key, value)
101+
}
102+
103+
// AddReqParam - add request query string parameter
104+
// supported key: see supportedQueryValues.
105+
// If an unsupported key is passed in, it will be ignored and nothing will be done.
106+
func (o *GetObjectOptions) AddReqParam(key, value string) {
107+
if !isStandardQueryValue(key) {
108+
// do nothing
109+
return
110+
}
111+
if o.reqParams == nil {
112+
o.reqParams = make(url.Values)
113+
}
114+
o.reqParams.Add(key, value)
115+
}
116+
86117
// SetMatchETag - set match etag.
87118
func (o *GetObjectOptions) SetMatchETag(etag string) error {
88119
if etag == "" {
@@ -149,3 +180,24 @@ func (o *GetObjectOptions) SetRange(start, end int64) error {
149180
}
150181
return nil
151182
}
183+
184+
// toQueryValues - Convert the versionId, partNumber, and reqParams in Options to query string parameters.
185+
func (o *GetObjectOptions) toQueryValues() url.Values {
186+
urlValues := make(url.Values)
187+
if o.VersionID != "" {
188+
urlValues.Set("versionId", o.VersionID)
189+
}
190+
if o.PartNumber > 0 {
191+
urlValues.Set("partNumber", strconv.Itoa(o.PartNumber))
192+
}
193+
194+
if o.reqParams != nil {
195+
for key, values := range o.reqParams {
196+
for _, value := range values {
197+
urlValues.Add(key, value)
198+
}
199+
}
200+
}
201+
202+
return urlValues
203+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//go:build example
2+
// +build example
3+
4+
/*
5+
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
6+
* Copyright 2015-2017 MinIO, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
package s3
22+
23+
import (
24+
"context"
25+
"io"
26+
"log"
27+
"os"
28+
29+
"github.com/minio/minio-go/v7"
30+
"github.com/minio/minio-go/v7/pkg/credentials"
31+
)
32+
33+
func main() {
34+
// Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY, my-bucketname, my-objectname and
35+
// my-testfile are dummy values, please replace them with original values.
36+
37+
// Requests are always secure (HTTPS) by default. Set secure=false to enable insecure (HTTP) access.
38+
// This boolean value is the last argument for New().
39+
40+
// New returns an Amazon S3 compatible client object. API compatibility (v2 or v4) is automatically
41+
// determined based on the Endpoint value.
42+
s3Client, err := minio.New("s3.amazonaws.com", &minio.Options{
43+
Creds: credentials.NewStaticV4("YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY", ""),
44+
Secure: true,
45+
})
46+
if err != nil {
47+
log.Fatalln(err)
48+
}
49+
50+
// response-cache-control=ResponseCacheControl
51+
// response-content-disposition=ResponseContentDisposition
52+
// response-content-encoding=ResponseContentEncoding
53+
// response-content-language=ResponseContentLanguage
54+
// response-content-type=ResponseContentType
55+
// response-expires=ResponseExpires
56+
opt := minio.GetObjectOptions{}
57+
opt.SetReqParam("response-content-disposition", `attachment; filename="testing.txt"`)
58+
opt.SetReqParam("response-cache-control", "No-cache")
59+
opt.SetReqParam("response-content-encoding", "x-gzip")
60+
opt.SetReqParam("response-expires", "Mon, 02 Jan 2006 15:04:05 GMT")
61+
reader, err := s3Client.GetObject(context.Background(), "my-bucketname", "my-objectname", opt)
62+
if err != nil {
63+
log.Fatalln(err)
64+
}
65+
defer reader.Close()
66+
67+
localFile, err := os.Create("my-testfile")
68+
if err != nil {
69+
log.Fatalln(err)
70+
}
71+
defer localFile.Close()
72+
73+
stat, err := reader.Stat()
74+
if err != nil {
75+
log.Fatalln(err)
76+
}
77+
78+
if _, err := io.CopyN(localFile, reader, stat.Size); err != nil {
79+
log.Fatalln(err)
80+
}
81+
}

examples/s3/go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ require (
2020
gopkg.in/ini.v1 v1.66.6 // indirect
2121
)
2222

23-
replace github.com/minio/minio-go/v7 v7.0.10 => ../..
23+
replace github.com/minio/minio-go/v7 v7.0.32 => ../..

examples/s3/go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
5050
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
5151
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
5252
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
53+
github.com/reedchan7/minio-go-sdk/v7 v7.0.48-0.20230116044606-89d027d0d418 h1:GwSqibsxYk62jRck8mt/HThq2LicP3A8pVtoWJ/WCeI=
54+
github.com/reedchan7/minio-go-sdk/v7 v7.0.48-0.20230116044606-89d027d0d418/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
5355
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
5456
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
5557
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=

utils.go

+17
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,23 @@ func isAmzHeader(headerKey string) bool {
511511
return strings.HasPrefix(key, "x-amz-meta-") || strings.HasPrefix(key, "x-amz-grant-") || key == "x-amz-acl" || isSSEHeader(headerKey) || strings.HasPrefix(key, "x-amz-checksum-")
512512
}
513513

514+
// supportedQueryValues is a list of query strings that can be passed in when using GetObject.
515+
var supportedQueryValues = map[string]bool{
516+
"partNumber": true,
517+
"versionId": true,
518+
"response-cache-control": true,
519+
"response-content-disposition": true,
520+
"response-content-encoding": true,
521+
"response-content-language": true,
522+
"response-content-type": true,
523+
"response-expires": true,
524+
}
525+
526+
// isStandardQueryValue will return true when the passed in query string parameter is supported rather than customized.
527+
func isStandardQueryValue(qsKey string) bool {
528+
return supportedQueryValues[qsKey]
529+
}
530+
514531
var (
515532
md5Pool = sync.Pool{New: func() interface{} { return md5.New() }}
516533
sha256Pool = sync.Pool{New: func() interface{} { return sha256.New() }}

0 commit comments

Comments
 (0)