mirror of
https://github.com/minio/minio.git
synced 2025-02-09 12:48:08 -05:00
Allow CopyObject() in S3 gateway to support metadata (#5000)
Fixes #4924
This commit is contained in:
parent
53f3d2fd65
commit
89d528a4ed
@ -55,11 +55,11 @@ func (l *s3Objects) AnonPutObject(bucket string, object string, size int64, data
|
|||||||
|
|
||||||
// AnonGetObject - Get object anonymously
|
// AnonGetObject - Get object anonymously
|
||||||
func (l *s3Objects) AnonGetObject(bucket string, key string, startOffset int64, length int64, writer io.Writer) error {
|
func (l *s3Objects) AnonGetObject(bucket string, key string, startOffset int64, length int64, writer io.Writer) error {
|
||||||
r := minio.NewGetReqHeaders()
|
opts := minio.GetObjectOptions{}
|
||||||
if err := r.SetRange(startOffset, startOffset+length-1); err != nil {
|
if err := opts.SetRange(startOffset, startOffset+length-1); err != nil {
|
||||||
return s3ToObjectError(traceError(err), bucket, key)
|
return s3ToObjectError(traceError(err), bucket, key)
|
||||||
}
|
}
|
||||||
object, _, err := l.anonClient.GetObject(bucket, key, r)
|
object, _, err := l.anonClient.GetObject(bucket, key, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s3ToObjectError(traceError(err), bucket, key)
|
return s3ToObjectError(traceError(err), bucket, key)
|
||||||
}
|
}
|
||||||
@ -75,8 +75,7 @@ func (l *s3Objects) AnonGetObject(bucket string, key string, startOffset int64,
|
|||||||
|
|
||||||
// AnonGetObjectInfo - Get object info anonymously
|
// AnonGetObjectInfo - Get object info anonymously
|
||||||
func (l *s3Objects) AnonGetObjectInfo(bucket string, object string) (objInfo ObjectInfo, e error) {
|
func (l *s3Objects) AnonGetObjectInfo(bucket string, object string) (objInfo ObjectInfo, e error) {
|
||||||
r := minio.NewHeadReqHeaders()
|
oi, err := l.anonClient.StatObject(bucket, object, minio.StatObjectOptions{})
|
||||||
oi, err := l.anonClient.StatObject(bucket, object, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return objInfo, s3ToObjectError(traceError(err), bucket, object)
|
return objInfo, s3ToObjectError(traceError(err), bucket, object)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
||||||
@ -290,22 +289,20 @@ func fromMinioClientListBucketResult(bucket string, result minio.ListBucketResul
|
|||||||
// startOffset indicates the starting read location of the object.
|
// startOffset indicates the starting read location of the object.
|
||||||
// length indicates the total length of the object.
|
// length indicates the total length of the object.
|
||||||
func (l *s3Objects) GetObject(bucket string, key string, startOffset int64, length int64, writer io.Writer) error {
|
func (l *s3Objects) GetObject(bucket string, key string, startOffset int64, length int64, writer io.Writer) error {
|
||||||
r := minio.NewGetReqHeaders()
|
|
||||||
|
|
||||||
if length < 0 && length != -1 {
|
if length < 0 && length != -1 {
|
||||||
return s3ToObjectError(traceError(errInvalidArgument), bucket, key)
|
return s3ToObjectError(traceError(errInvalidArgument), bucket, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts := minio.GetObjectOptions{}
|
||||||
if startOffset >= 0 && length >= 0 {
|
if startOffset >= 0 && length >= 0 {
|
||||||
if err := r.SetRange(startOffset, startOffset+length-1); err != nil {
|
if err := opts.SetRange(startOffset, startOffset+length-1); err != nil {
|
||||||
return s3ToObjectError(traceError(err), bucket, key)
|
return s3ToObjectError(traceError(err), bucket, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object, _, err := l.Client.GetObject(bucket, key, r)
|
object, _, err := l.Client.GetObject(bucket, key, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s3ToObjectError(traceError(err), bucket, key)
|
return s3ToObjectError(traceError(err), bucket, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer object.Close()
|
defer object.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(writer, object); err != nil {
|
if _, err := io.Copy(writer, object); err != nil {
|
||||||
@ -333,8 +330,7 @@ func fromMinioClientObjectInfo(bucket string, oi minio.ObjectInfo) ObjectInfo {
|
|||||||
|
|
||||||
// GetObjectInfo reads object info and replies back ObjectInfo
|
// GetObjectInfo reads object info and replies back ObjectInfo
|
||||||
func (l *s3Objects) GetObjectInfo(bucket string, object string) (objInfo ObjectInfo, err error) {
|
func (l *s3Objects) GetObjectInfo(bucket string, object string) (objInfo ObjectInfo, err error) {
|
||||||
r := minio.NewHeadReqHeaders()
|
oi, err := l.Client.StatObject(bucket, object, minio.StatObjectOptions{})
|
||||||
oi, err := l.Client.StatObject(bucket, object, r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ObjectInfo{}, s3ToObjectError(traceError(err), bucket, object)
|
return ObjectInfo{}, s3ToObjectError(traceError(err), bucket, object)
|
||||||
}
|
}
|
||||||
@ -361,35 +357,17 @@ func (l *s3Objects) PutObject(bucket string, object string, data *HashReader, me
|
|||||||
return fromMinioClientObjectInfo(bucket, oi), nil
|
return fromMinioClientObjectInfo(bucket, oi), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyObject copies a blob from source container to destination container.
|
// CopyObject copies an object from source bucket to a destination bucket.
|
||||||
func (l *s3Objects) CopyObject(srcBucket string, srcObject string, dstBucket string, dstObject string, metadata map[string]string) (objInfo ObjectInfo, err error) {
|
func (l *s3Objects) CopyObject(srcBucket string, srcObject string, dstBucket string, dstObject string, metadata map[string]string) (objInfo ObjectInfo, err error) {
|
||||||
// Source object
|
// Set this header such that following CopyObject() always sets the right metadata on the destination.
|
||||||
src := minio.NewSourceInfo(srcBucket, srcObject, nil)
|
// metadata input is already a trickled down value from interpreting x-amz-metadata-directive at
|
||||||
|
// handler layer. So what we have right now is supposed to be applied on the destination object anyways.
|
||||||
// Destination object
|
// So preserve it by adding "REPLACE" directive to save all the metadata set by CopyObject API.
|
||||||
var xamzMeta = map[string]string{}
|
metadata["x-amz-metadata-directive"] = "REPLACE"
|
||||||
for key := range metadata {
|
if _, err = l.Client.CopyObject(srcBucket, srcObject, dstBucket, dstObject, metadata); err != nil {
|
||||||
for _, prefix := range userMetadataKeyPrefixes {
|
|
||||||
if strings.HasPrefix(key, prefix) {
|
|
||||||
xamzMeta[key] = metadata[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst, err := minio.NewDestinationInfo(dstBucket, dstObject, nil, xamzMeta)
|
|
||||||
if err != nil {
|
|
||||||
return objInfo, s3ToObjectError(traceError(err), dstBucket, dstObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = l.Client.CopyObject(dst, src); err != nil {
|
|
||||||
return objInfo, s3ToObjectError(traceError(err), srcBucket, srcObject)
|
return objInfo, s3ToObjectError(traceError(err), srcBucket, srcObject)
|
||||||
}
|
}
|
||||||
|
return l.GetObjectInfo(dstBucket, dstObject)
|
||||||
oi, err := l.GetObjectInfo(dstBucket, dstObject)
|
|
||||||
if err != nil {
|
|
||||||
return objInfo, s3ToObjectError(traceError(err), dstBucket, dstObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
return oi, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteObject deletes a blob in bucket
|
// DeleteObject deletes a blob in bucket
|
||||||
|
@ -398,7 +398,9 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err, "found invalid http request header")
|
errorIf(err, "found invalid http request header")
|
||||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if x-amz-metadata-directive was not set to REPLACE and source,
|
// Check if x-amz-metadata-directive was not set to REPLACE and source,
|
||||||
// desination are same objects.
|
// desination are same objects.
|
||||||
if !isMetadataReplace(r.Header) && cpSrcDstSame {
|
if !isMetadataReplace(r.Header) && cpSrcDstSame {
|
||||||
|
1
vendor/github.com/minio/minio-go/README.md
generated
vendored
1
vendor/github.com/minio/minio-go/README.md
generated
vendored
@ -224,6 +224,7 @@ The full API Reference is available here.
|
|||||||
### Full Examples : Encrypted Object Operations
|
### Full Examples : Encrypted Object Operations
|
||||||
* [put-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/put-encrypted-object.go)
|
* [put-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/put-encrypted-object.go)
|
||||||
* [get-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/get-encrypted-object.go)
|
* [get-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/get-encrypted-object.go)
|
||||||
|
* [fput-encrypted-object.go](https://github.com/minio/minio-go/blob/master/examples/s3/fputencrypted-object.go)
|
||||||
|
|
||||||
### Full Examples : Presigned Operations
|
### Full Examples : Presigned Operations
|
||||||
* [presignedgetobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedgetobject.go)
|
* [presignedgetobject.go](https://github.com/minio/minio-go/blob/master/examples/s3/presignedgetobject.go)
|
||||||
|
51
vendor/github.com/minio/minio-go/api-compose-object.go
generated
vendored
51
vendor/github.com/minio/minio-go/api-compose-object.go
generated
vendored
@ -244,11 +244,11 @@ func (s *SourceInfo) getProps(c Client) (size int64, etag string, userMeta map[s
|
|||||||
// Get object info - need size and etag here. Also, decryption
|
// Get object info - need size and etag here. Also, decryption
|
||||||
// headers are added to the stat request if given.
|
// headers are added to the stat request if given.
|
||||||
var objInfo ObjectInfo
|
var objInfo ObjectInfo
|
||||||
rh := NewGetReqHeaders()
|
opts := StatObjectOptions{}
|
||||||
for k, v := range s.decryptKey.getSSEHeaders(false) {
|
for k, v := range s.decryptKey.getSSEHeaders(false) {
|
||||||
rh.Set(k, v)
|
opts.Set(k, v)
|
||||||
}
|
}
|
||||||
objInfo, err = c.statObject(s.bucket, s.object, rh)
|
objInfo, err = c.statObject(s.bucket, s.object, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Could not stat object - %s/%s: %v", s.bucket, s.object, err)
|
err = fmt.Errorf("Could not stat object - %s/%s: %v", s.bucket, s.object, err)
|
||||||
} else {
|
} else {
|
||||||
@ -266,6 +266,51 @@ func (s *SourceInfo) getProps(c Client) (size int64, etag string, userMeta map[s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Low level implementation of CopyObject API, supports only upto 5GiB worth of copy.
|
||||||
|
func (c Client) copyObjectDo(ctx context.Context, srcBucket, srcObject, destBucket, destObject string,
|
||||||
|
metadata map[string]string) (ObjectInfo, error) {
|
||||||
|
|
||||||
|
// Build headers.
|
||||||
|
headers := make(http.Header)
|
||||||
|
|
||||||
|
// Set all the metadata headers.
|
||||||
|
for k, v := range metadata {
|
||||||
|
headers.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the source header
|
||||||
|
headers.Set("x-amz-copy-source", s3utils.EncodePath(srcBucket+"/"+srcObject))
|
||||||
|
|
||||||
|
// Send upload-part-copy request
|
||||||
|
resp, err := c.executeMethod(ctx, "PUT", requestMetadata{
|
||||||
|
bucketName: destBucket,
|
||||||
|
objectName: destObject,
|
||||||
|
customHeader: headers,
|
||||||
|
})
|
||||||
|
defer closeResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
return ObjectInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we got an error response.
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return ObjectInfo{}, httpRespToErrorResponse(resp, srcBucket, srcObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpObjRes := copyObjectResult{}
|
||||||
|
err = xmlDecoder(resp.Body, &cpObjRes)
|
||||||
|
if err != nil {
|
||||||
|
return ObjectInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
objInfo := ObjectInfo{
|
||||||
|
Key: destObject,
|
||||||
|
ETag: strings.Trim(cpObjRes.ETag, "\""),
|
||||||
|
LastModified: cpObjRes.LastModified,
|
||||||
|
}
|
||||||
|
return objInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
// uploadPartCopy - helper function to create a part in a multipart
|
// uploadPartCopy - helper function to create a part in a multipart
|
||||||
// upload via an upload-part-copy request
|
// upload via an upload-part-copy request
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html
|
// https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html
|
||||||
|
5
vendor/github.com/minio/minio-go/api-get-object-context.go
generated
vendored
5
vendor/github.com/minio/minio-go/api-get-object-context.go
generated
vendored
@ -19,6 +19,7 @@ package minio
|
|||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
// GetObjectWithContext - returns an seekable, readable object.
|
// GetObjectWithContext - returns an seekable, readable object.
|
||||||
func (c Client) GetObjectWithContext(ctx context.Context, bucketName, objectName string) (*Object, error) {
|
// The options can be used to specify the GET request further.
|
||||||
return c.getObjectWithContext(ctx, bucketName, objectName)
|
func (c Client) GetObjectWithContext(ctx context.Context, bucketName, objectName string, opts GetObjectOptions) (*Object, error) {
|
||||||
|
return c.getObjectWithContext(ctx, bucketName, objectName, opts)
|
||||||
}
|
}
|
||||||
|
28
vendor/github.com/minio/minio-go/api-get-object-file.go
generated
vendored
28
vendor/github.com/minio/minio-go/api-get-object-file.go
generated
vendored
@ -21,23 +21,34 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/pkg/encrypt"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/minio/minio-go/pkg/s3utils"
|
"github.com/minio/minio-go/pkg/s3utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FGetObjectWithContext - download contents of an object to a local file.
|
// FGetObjectWithContext - download contents of an object to a local file.
|
||||||
func (c Client) FGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string) error {
|
// The options can be used to specify the GET request further.
|
||||||
return c.fGetObjectWithContext(ctx, bucketName, objectName, filePath)
|
func (c Client) FGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error {
|
||||||
|
return c.fGetObjectWithContext(ctx, bucketName, objectName, filePath, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FGetObject - download contents of an object to a local file.
|
// FGetObject - download contents of an object to a local file.
|
||||||
func (c Client) FGetObject(bucketName, objectName, filePath string) error {
|
func (c Client) FGetObject(bucketName, objectName, filePath string, opts GetObjectOptions) error {
|
||||||
return c.fGetObjectWithContext(context.Background(), bucketName, objectName, filePath)
|
return c.fGetObjectWithContext(context.Background(), bucketName, objectName, filePath, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FGetEncryptedObject - Decrypt and store an object at filePath.
|
||||||
|
func (c Client) FGetEncryptedObject(bucketName, objectName, filePath string, materials encrypt.Materials) error {
|
||||||
|
if materials == nil {
|
||||||
|
return ErrInvalidArgument("Unable to recognize empty encryption properties")
|
||||||
|
}
|
||||||
|
return c.FGetObject(bucketName, objectName, filePath, GetObjectOptions{Materials: materials})
|
||||||
}
|
}
|
||||||
|
|
||||||
// fGetObjectWithContext - fgetObject wrapper function with context
|
// fGetObjectWithContext - fgetObject wrapper function with context
|
||||||
func (c Client) fGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string) error {
|
func (c Client) fGetObjectWithContext(ctx context.Context, bucketName, objectName, filePath string, opts GetObjectOptions) error {
|
||||||
// Input validation.
|
// Input validation.
|
||||||
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -72,7 +83,7 @@ func (c Client) fGetObjectWithContext(ctx context.Context, bucketName, objectNam
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gather md5sum.
|
// Gather md5sum.
|
||||||
objectStat, err := c.StatObject(bucketName, objectName)
|
objectStat, err := c.StatObject(bucketName, objectName, StatObjectOptions{opts})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -94,13 +105,12 @@ func (c Client) fGetObjectWithContext(ctx context.Context, bucketName, objectNam
|
|||||||
|
|
||||||
// Initialize get object request headers to set the
|
// Initialize get object request headers to set the
|
||||||
// appropriate range offsets to read from.
|
// appropriate range offsets to read from.
|
||||||
reqHeaders := NewGetReqHeaders()
|
|
||||||
if st.Size() > 0 {
|
if st.Size() > 0 {
|
||||||
reqHeaders.SetRange(st.Size(), 0)
|
opts.SetRange(st.Size(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek to current position for incoming reader.
|
// Seek to current position for incoming reader.
|
||||||
objectReader, objectStat, err := c.getObject(ctx, bucketName, objectName, reqHeaders)
|
objectReader, objectStat, err := c.getObject(ctx, bucketName, objectName, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
88
vendor/github.com/minio/minio-go/api-get-object.go
generated
vendored
88
vendor/github.com/minio/minio-go/api-get-object.go
generated
vendored
@ -37,32 +37,16 @@ func (c Client) GetEncryptedObject(bucketName, objectName string, encryptMateria
|
|||||||
return nil, ErrInvalidArgument("Unable to recognize empty encryption properties")
|
return nil, ErrInvalidArgument("Unable to recognize empty encryption properties")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch encrypted object
|
return c.GetObject(bucketName, objectName, GetObjectOptions{Materials: encryptMaterials})
|
||||||
encReader, err := c.GetObject(bucketName, objectName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Stat object to get its encryption metadata
|
|
||||||
st, err := encReader.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup object for decrytion, object is transparently
|
|
||||||
// decrypted as the consumer starts reading.
|
|
||||||
encryptMaterials.SetupDecryptMode(encReader, st.Metadata.Get(amzHeaderIV), st.Metadata.Get(amzHeaderKey))
|
|
||||||
|
|
||||||
// Success.
|
|
||||||
return encryptMaterials, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObject - returns an seekable, readable object.
|
// GetObject - returns an seekable, readable object.
|
||||||
func (c Client) GetObject(bucketName, objectName string) (*Object, error) {
|
func (c Client) GetObject(bucketName, objectName string, opts GetObjectOptions) (*Object, error) {
|
||||||
return c.getObjectWithContext(context.Background(), bucketName, objectName)
|
return c.getObjectWithContext(context.Background(), bucketName, objectName, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObject wrapper function that accepts a request context
|
// GetObject wrapper function that accepts a request context
|
||||||
func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName string) (*Object, error) {
|
func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName string, opts GetObjectOptions) (*Object, error) {
|
||||||
// Input validation.
|
// Input validation.
|
||||||
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -108,34 +92,26 @@ func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName
|
|||||||
if req.isFirstReq {
|
if req.isFirstReq {
|
||||||
// First request is a Read/ReadAt.
|
// First request is a Read/ReadAt.
|
||||||
if req.isReadOp {
|
if req.isReadOp {
|
||||||
reqHeaders := NewGetReqHeaders()
|
|
||||||
// Differentiate between wanting the whole object and just a range.
|
// Differentiate between wanting the whole object and just a range.
|
||||||
if req.isReadAt {
|
if req.isReadAt {
|
||||||
// If this is a ReadAt request only get the specified range.
|
// If this is a ReadAt request only get the specified range.
|
||||||
// Range is set with respect to the offset and length of the buffer requested.
|
// Range is set with respect to the offset and length of the buffer requested.
|
||||||
// Do not set objectInfo from the first readAt request because it will not get
|
// Do not set objectInfo from the first readAt request because it will not get
|
||||||
// the whole object.
|
// the whole object.
|
||||||
reqHeaders.SetRange(req.Offset, req.Offset+int64(len(req.Buffer))-1)
|
opts.SetRange(req.Offset, req.Offset+int64(len(req.Buffer))-1)
|
||||||
httpReader, objectInfo, err = c.getObject(ctx, bucketName, objectName, reqHeaders)
|
} else if req.Offset > 0 {
|
||||||
} else {
|
opts.SetRange(req.Offset, 0)
|
||||||
if req.Offset > 0 {
|
|
||||||
reqHeaders.SetRange(req.Offset, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First request is a Read request.
|
|
||||||
httpReader, objectInfo, err = c.getObject(ctx, bucketName, objectName, reqHeaders)
|
|
||||||
}
|
}
|
||||||
|
httpReader, objectInfo, err = c.getObject(ctx, bucketName, objectName, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resCh <- getResponse{
|
resCh <- getResponse{Error: err}
|
||||||
Error: err,
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
etag = objectInfo.ETag
|
etag = objectInfo.ETag
|
||||||
// Read at least firstReq.Buffer bytes, if not we have
|
// Read at least firstReq.Buffer bytes, if not we have
|
||||||
// reached our EOF.
|
// reached our EOF.
|
||||||
size, err := io.ReadFull(httpReader, req.Buffer)
|
size, err := io.ReadFull(httpReader, req.Buffer)
|
||||||
if err == io.ErrUnexpectedEOF {
|
if size > 0 && err == io.ErrUnexpectedEOF {
|
||||||
// If an EOF happens after reading some but not
|
// If an EOF happens after reading some but not
|
||||||
// all the bytes ReadFull returns ErrUnexpectedEOF
|
// all the bytes ReadFull returns ErrUnexpectedEOF
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
@ -150,7 +126,7 @@ func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName
|
|||||||
} else {
|
} else {
|
||||||
// First request is a Stat or Seek call.
|
// First request is a Stat or Seek call.
|
||||||
// Only need to run a StatObject until an actual Read or ReadAt request comes through.
|
// Only need to run a StatObject until an actual Read or ReadAt request comes through.
|
||||||
objectInfo, err = c.StatObject(bucketName, objectName)
|
objectInfo, err = c.statObject(bucketName, objectName, StatObjectOptions{opts})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resCh <- getResponse{
|
resCh <- getResponse{
|
||||||
Error: err,
|
Error: err,
|
||||||
@ -165,11 +141,10 @@ func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if req.settingObjectInfo { // Request is just to get objectInfo.
|
} else if req.settingObjectInfo { // Request is just to get objectInfo.
|
||||||
reqHeaders := NewGetReqHeaders()
|
|
||||||
if etag != "" {
|
if etag != "" {
|
||||||
reqHeaders.SetMatchETag(etag)
|
opts.SetMatchETag(etag)
|
||||||
}
|
}
|
||||||
objectInfo, err := c.statObject(bucketName, objectName, reqHeaders)
|
objectInfo, err := c.statObject(bucketName, objectName, StatObjectOptions{opts})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resCh <- getResponse{
|
resCh <- getResponse{
|
||||||
Error: err,
|
Error: err,
|
||||||
@ -189,9 +164,8 @@ func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName
|
|||||||
// new ones when they haven't been already.
|
// new ones when they haven't been already.
|
||||||
// All readAt requests are new requests.
|
// All readAt requests are new requests.
|
||||||
if req.DidOffsetChange || !req.beenRead {
|
if req.DidOffsetChange || !req.beenRead {
|
||||||
reqHeaders := NewGetReqHeaders()
|
|
||||||
if etag != "" {
|
if etag != "" {
|
||||||
reqHeaders.SetMatchETag(etag)
|
opts.SetMatchETag(etag)
|
||||||
}
|
}
|
||||||
if httpReader != nil {
|
if httpReader != nil {
|
||||||
// Close previously opened http reader.
|
// Close previously opened http reader.
|
||||||
@ -200,16 +174,11 @@ func (c Client) getObjectWithContext(ctx context.Context, bucketName, objectName
|
|||||||
// If this request is a readAt only get the specified range.
|
// If this request is a readAt only get the specified range.
|
||||||
if req.isReadAt {
|
if req.isReadAt {
|
||||||
// Range is set with respect to the offset and length of the buffer requested.
|
// Range is set with respect to the offset and length of the buffer requested.
|
||||||
reqHeaders.SetRange(req.Offset, req.Offset+int64(len(req.Buffer))-1)
|
opts.SetRange(req.Offset, req.Offset+int64(len(req.Buffer))-1)
|
||||||
httpReader, _, err = c.getObject(ctx, bucketName, objectName, reqHeaders)
|
} else if req.Offset > 0 { // Range is set with respect to the offset.
|
||||||
} else {
|
opts.SetRange(req.Offset, 0)
|
||||||
// Range is set with respect to the offset.
|
|
||||||
if req.Offset > 0 {
|
|
||||||
reqHeaders.SetRange(req.Offset, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
httpReader, objectInfo, err = c.getObject(ctx, bucketName, objectName, reqHeaders)
|
|
||||||
}
|
}
|
||||||
|
httpReader, objectInfo, err = c.getObject(ctx, bucketName, objectName, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resCh <- getResponse{
|
resCh <- getResponse{
|
||||||
Error: err,
|
Error: err,
|
||||||
@ -632,7 +601,7 @@ func newObject(reqCh chan<- getRequest, resCh <-chan getResponse, doneCh chan<-
|
|||||||
//
|
//
|
||||||
// For more information about the HTTP Range header.
|
// For more information about the HTTP Range header.
|
||||||
// go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.
|
// go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.
|
||||||
func (c Client) getObject(ctx context.Context, bucketName, objectName string, reqHeaders RequestHeaders) (io.ReadCloser, ObjectInfo, error) {
|
func (c Client) getObject(ctx context.Context, bucketName, objectName string, opts GetObjectOptions) (io.ReadCloser, ObjectInfo, error) {
|
||||||
// Validate input arguments.
|
// Validate input arguments.
|
||||||
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
||||||
return nil, ObjectInfo{}, err
|
return nil, ObjectInfo{}, err
|
||||||
@ -641,17 +610,11 @@ func (c Client) getObject(ctx context.Context, bucketName, objectName string, re
|
|||||||
return nil, ObjectInfo{}, err
|
return nil, ObjectInfo{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set all the necessary reqHeaders.
|
|
||||||
customHeader := make(http.Header)
|
|
||||||
for key, value := range reqHeaders.Header {
|
|
||||||
customHeader[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute GET on objectName.
|
// Execute GET on objectName.
|
||||||
resp, err := c.executeMethod(ctx, "GET", requestMetadata{
|
resp, err := c.executeMethod(ctx, "GET", requestMetadata{
|
||||||
bucketName: bucketName,
|
bucketName: bucketName,
|
||||||
objectName: objectName,
|
objectName: objectName,
|
||||||
customHeader: customHeader,
|
customHeader: opts.Header(),
|
||||||
contentSHA256Bytes: emptySHA256,
|
contentSHA256Bytes: emptySHA256,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -698,6 +661,15 @@ func (c Client) getObject(ctx context.Context, bucketName, objectName string, re
|
|||||||
Metadata: extractObjMetadata(resp.Header),
|
Metadata: extractObjMetadata(resp.Header),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reader := resp.Body
|
||||||
|
if opts.Materials != nil {
|
||||||
|
err = opts.Materials.SetupDecryptMode(reader, objectStat.Metadata.Get(amzHeaderIV), objectStat.Metadata.Get(amzHeaderKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, ObjectInfo{}, err
|
||||||
|
}
|
||||||
|
reader = opts.Materials
|
||||||
|
}
|
||||||
|
|
||||||
// do not close body here, caller will close
|
// do not close body here, caller will close
|
||||||
return resp.Body, objectStat, nil
|
return reader, objectStat, nil
|
||||||
}
|
}
|
||||||
|
@ -20,80 +20,94 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/minio/minio-go/pkg/encrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequestHeaders - implement methods for setting special
|
// GetObjectOptions are used to specify additional headers or options
|
||||||
// request headers for GET, HEAD object operations.
|
// during GET requests.
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
|
type GetObjectOptions struct {
|
||||||
type RequestHeaders struct {
|
headers map[string]string
|
||||||
http.Header
|
|
||||||
|
Materials encrypt.Materials
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGetReqHeaders - initializes a new request headers for GET request.
|
// StatObjectOptions are used to specify additional headers or options
|
||||||
func NewGetReqHeaders() RequestHeaders {
|
// during GET info/stat requests.
|
||||||
return RequestHeaders{
|
type StatObjectOptions struct {
|
||||||
Header: make(http.Header),
|
GetObjectOptions
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHeadReqHeaders - initializes a new request headers for HEAD request.
|
// Header returns the http.Header representation of the GET options.
|
||||||
func NewHeadReqHeaders() RequestHeaders {
|
func (o GetObjectOptions) Header() http.Header {
|
||||||
return RequestHeaders{
|
headers := make(http.Header, len(o.headers))
|
||||||
Header: make(http.Header),
|
for k, v := range o.headers {
|
||||||
|
headers.Set(k, v)
|
||||||
}
|
}
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adds a key value pair to the options. The
|
||||||
|
// key-value pair will be part of the HTTP GET request
|
||||||
|
// headers.
|
||||||
|
func (o *GetObjectOptions) Set(key, value string) {
|
||||||
|
if o.headers == nil {
|
||||||
|
o.headers = make(map[string]string)
|
||||||
|
}
|
||||||
|
o.headers[http.CanonicalHeaderKey(key)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMatchETag - set match etag.
|
// SetMatchETag - set match etag.
|
||||||
func (c RequestHeaders) SetMatchETag(etag string) error {
|
func (o *GetObjectOptions) SetMatchETag(etag string) error {
|
||||||
if etag == "" {
|
if etag == "" {
|
||||||
return ErrInvalidArgument("ETag cannot be empty.")
|
return ErrInvalidArgument("ETag cannot be empty.")
|
||||||
}
|
}
|
||||||
c.Set("If-Match", "\""+etag+"\"")
|
o.Set("If-Match", "\""+etag+"\"")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMatchETagExcept - set match etag except.
|
// SetMatchETagExcept - set match etag except.
|
||||||
func (c RequestHeaders) SetMatchETagExcept(etag string) error {
|
func (o *GetObjectOptions) SetMatchETagExcept(etag string) error {
|
||||||
if etag == "" {
|
if etag == "" {
|
||||||
return ErrInvalidArgument("ETag cannot be empty.")
|
return ErrInvalidArgument("ETag cannot be empty.")
|
||||||
}
|
}
|
||||||
c.Set("If-None-Match", "\""+etag+"\"")
|
o.Set("If-None-Match", "\""+etag+"\"")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetUnmodified - set unmodified time since.
|
// SetUnmodified - set unmodified time since.
|
||||||
func (c RequestHeaders) SetUnmodified(modTime time.Time) error {
|
func (o *GetObjectOptions) SetUnmodified(modTime time.Time) error {
|
||||||
if modTime.IsZero() {
|
if modTime.IsZero() {
|
||||||
return ErrInvalidArgument("Modified since cannot be empty.")
|
return ErrInvalidArgument("Modified since cannot be empty.")
|
||||||
}
|
}
|
||||||
c.Set("If-Unmodified-Since", modTime.Format(http.TimeFormat))
|
o.Set("If-Unmodified-Since", modTime.Format(http.TimeFormat))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetModified - set modified time since.
|
// SetModified - set modified time since.
|
||||||
func (c RequestHeaders) SetModified(modTime time.Time) error {
|
func (o *GetObjectOptions) SetModified(modTime time.Time) error {
|
||||||
if modTime.IsZero() {
|
if modTime.IsZero() {
|
||||||
return ErrInvalidArgument("Modified since cannot be empty.")
|
return ErrInvalidArgument("Modified since cannot be empty.")
|
||||||
}
|
}
|
||||||
c.Set("If-Modified-Since", modTime.Format(http.TimeFormat))
|
o.Set("If-Modified-Since", modTime.Format(http.TimeFormat))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRange - set the start and end offset of the object to be read.
|
// SetRange - set the start and end offset of the object to be read.
|
||||||
// See https://tools.ietf.org/html/rfc7233#section-3.1 for reference.
|
// See https://tools.ietf.org/html/rfc7233#section-3.1 for reference.
|
||||||
func (c RequestHeaders) SetRange(start, end int64) error {
|
func (o *GetObjectOptions) SetRange(start, end int64) error {
|
||||||
switch {
|
switch {
|
||||||
case start == 0 && end < 0:
|
case start == 0 && end < 0:
|
||||||
// Read last '-end' bytes. `bytes=-N`.
|
// Read last '-end' bytes. `bytes=-N`.
|
||||||
c.Set("Range", fmt.Sprintf("bytes=%d", end))
|
o.Set("Range", fmt.Sprintf("bytes=%d", end))
|
||||||
case 0 < start && end == 0:
|
case 0 < start && end == 0:
|
||||||
// Read everything starting from offset
|
// Read everything starting from offset
|
||||||
// 'start'. `bytes=N-`.
|
// 'start'. `bytes=N-`.
|
||||||
c.Set("Range", fmt.Sprintf("bytes=%d-", start))
|
o.Set("Range", fmt.Sprintf("bytes=%d-", start))
|
||||||
case 0 <= start && start <= end:
|
case 0 <= start && start <= end:
|
||||||
// Read everything starting at 'start' till the
|
// Read everything starting at 'start' till the
|
||||||
// 'end'. `bytes=N-M`
|
// 'end'. `bytes=N-M`
|
||||||
c.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))
|
o.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))
|
||||||
default:
|
default:
|
||||||
// All other cases such as
|
// All other cases such as
|
||||||
// bytes=-3-
|
// bytes=-3-
|
2
vendor/github.com/minio/minio-go/api-s3-datatypes.go
generated
vendored
2
vendor/github.com/minio/minio-go/api-s3-datatypes.go
generated
vendored
@ -128,7 +128,7 @@ type initiator struct {
|
|||||||
// copyObjectResult container for copy object response.
|
// copyObjectResult container for copy object response.
|
||||||
type copyObjectResult struct {
|
type copyObjectResult struct {
|
||||||
ETag string
|
ETag string
|
||||||
LastModified string // time string format "2006-01-02T15:04:05.000Z"
|
LastModified time.Time // time string format "2006-01-02T15:04:05.000Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObjectPart container for particular part of an object.
|
// ObjectPart container for particular part of an object.
|
||||||
|
14
vendor/github.com/minio/minio-go/api-stat.go
generated
vendored
14
vendor/github.com/minio/minio-go/api-stat.go
generated
vendored
@ -81,7 +81,7 @@ func extractObjMetadata(header http.Header) http.Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StatObject verifies if object exists and you have permission to access.
|
// StatObject verifies if object exists and you have permission to access.
|
||||||
func (c Client) StatObject(bucketName, objectName string) (ObjectInfo, error) {
|
func (c Client) StatObject(bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
|
||||||
// Input validation.
|
// Input validation.
|
||||||
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
||||||
return ObjectInfo{}, err
|
return ObjectInfo{}, err
|
||||||
@ -89,12 +89,11 @@ func (c Client) StatObject(bucketName, objectName string) (ObjectInfo, error) {
|
|||||||
if err := s3utils.CheckValidObjectName(objectName); err != nil {
|
if err := s3utils.CheckValidObjectName(objectName); err != nil {
|
||||||
return ObjectInfo{}, err
|
return ObjectInfo{}, err
|
||||||
}
|
}
|
||||||
reqHeaders := NewHeadReqHeaders()
|
return c.statObject(bucketName, objectName, opts)
|
||||||
return c.statObject(bucketName, objectName, reqHeaders)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower level API for statObject supporting pre-conditions and range headers.
|
// Lower level API for statObject supporting pre-conditions and range headers.
|
||||||
func (c Client) statObject(bucketName, objectName string, reqHeaders RequestHeaders) (ObjectInfo, error) {
|
func (c Client) statObject(bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
|
||||||
// Input validation.
|
// Input validation.
|
||||||
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
if err := s3utils.CheckValidBucketName(bucketName); err != nil {
|
||||||
return ObjectInfo{}, err
|
return ObjectInfo{}, err
|
||||||
@ -103,17 +102,12 @@ func (c Client) statObject(bucketName, objectName string, reqHeaders RequestHead
|
|||||||
return ObjectInfo{}, err
|
return ObjectInfo{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
customHeader := make(http.Header)
|
|
||||||
for k, v := range reqHeaders.Header {
|
|
||||||
customHeader[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute HEAD on objectName.
|
// Execute HEAD on objectName.
|
||||||
resp, err := c.executeMethod(context.Background(), "HEAD", requestMetadata{
|
resp, err := c.executeMethod(context.Background(), "HEAD", requestMetadata{
|
||||||
bucketName: bucketName,
|
bucketName: bucketName,
|
||||||
objectName: objectName,
|
objectName: objectName,
|
||||||
contentSHA256Bytes: emptySHA256,
|
contentSHA256Bytes: emptySHA256,
|
||||||
customHeader: customHeader,
|
customHeader: opts.Header(),
|
||||||
})
|
})
|
||||||
defer closeResponse(resp)
|
defer closeResponse(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
32
vendor/github.com/minio/minio-go/core.go
generated
vendored
32
vendor/github.com/minio/minio-go/core.go
generated
vendored
@ -19,6 +19,7 @@ package minio
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/minio/minio-go/pkg/policy"
|
"github.com/minio/minio-go/pkg/policy"
|
||||||
)
|
)
|
||||||
@ -53,9 +54,30 @@ func (c Core) ListObjectsV2(bucketName, objectPrefix, continuationToken string,
|
|||||||
return c.listObjectsV2Query(bucketName, objectPrefix, continuationToken, fetchOwner, delimiter, maxkeys)
|
return c.listObjectsV2Query(bucketName, objectPrefix, continuationToken, fetchOwner, delimiter, maxkeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyObject - copies an object from source object to destination object on server side.
|
||||||
|
func (c Core) CopyObject(sourceBucket, sourceObject, destBucket, destObject string, metadata map[string]string) (ObjectInfo, error) {
|
||||||
|
return c.copyObjectDo(context.Background(), sourceBucket, sourceObject, destBucket, destObject, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
// PutObject - Upload object. Uploads using single PUT call.
|
// PutObject - Upload object. Uploads using single PUT call.
|
||||||
func (c Core) PutObject(bucket, object string, data io.Reader, size int64, md5Sum, sha256Sum []byte, metadata map[string]string) (ObjectInfo, error) {
|
func (c Core) PutObject(bucket, object string, data io.Reader, size int64, md5Sum, sha256Sum []byte, metadata map[string]string) (ObjectInfo, error) {
|
||||||
return c.putObjectDo(context.Background(), bucket, object, data, md5Sum, sha256Sum, size, PutObjectOptions{UserMetadata: metadata})
|
opts := PutObjectOptions{}
|
||||||
|
m := make(map[string]string)
|
||||||
|
for k, v := range metadata {
|
||||||
|
if strings.ToLower(k) == "content-encoding" {
|
||||||
|
opts.ContentEncoding = v
|
||||||
|
} else if strings.ToLower(k) == "content-disposition" {
|
||||||
|
opts.ContentDisposition = v
|
||||||
|
} else if strings.ToLower(k) == "content-type" {
|
||||||
|
opts.ContentType = v
|
||||||
|
} else if strings.ToLower(k) == "cache-control" {
|
||||||
|
opts.CacheControl = v
|
||||||
|
} else {
|
||||||
|
m[k] = metadata[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opts.UserMetadata = m
|
||||||
|
return c.putObjectDo(context.Background(), bucket, object, data, md5Sum, sha256Sum, size, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMultipartUpload - Initiates new multipart upload and returns the new uploadID.
|
// NewMultipartUpload - Initiates new multipart upload and returns the new uploadID.
|
||||||
@ -111,12 +133,12 @@ func (c Core) PutBucketPolicy(bucket string, bucketPolicy policy.BucketAccessPol
|
|||||||
// GetObject is a lower level API implemented to support reading
|
// GetObject is a lower level API implemented to support reading
|
||||||
// partial objects and also downloading objects with special conditions
|
// partial objects and also downloading objects with special conditions
|
||||||
// matching etag, modtime etc.
|
// matching etag, modtime etc.
|
||||||
func (c Core) GetObject(bucketName, objectName string, reqHeaders RequestHeaders) (io.ReadCloser, ObjectInfo, error) {
|
func (c Core) GetObject(bucketName, objectName string, opts GetObjectOptions) (io.ReadCloser, ObjectInfo, error) {
|
||||||
return c.getObject(context.Background(), bucketName, objectName, reqHeaders)
|
return c.getObject(context.Background(), bucketName, objectName, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatObject is a lower level API implemented to support special
|
// StatObject is a lower level API implemented to support special
|
||||||
// conditions matching etag, modtime on a request.
|
// conditions matching etag, modtime on a request.
|
||||||
func (c Core) StatObject(bucketName, objectName string, reqHeaders RequestHeaders) (ObjectInfo, error) {
|
func (c Core) StatObject(bucketName, objectName string, opts StatObjectOptions) (ObjectInfo, error) {
|
||||||
return c.statObject(bucketName, objectName, reqHeaders)
|
return c.statObject(bucketName, objectName, opts)
|
||||||
}
|
}
|
||||||
|
5889
vendor/github.com/minio/minio-go/functional_tests.go
generated
vendored
5889
vendor/github.com/minio/minio-go/functional_tests.go
generated
vendored
File diff suppressed because it is too large
Load Diff
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
@ -336,10 +336,10 @@
|
|||||||
"revisionTime": "2016-02-29T08:42:30-08:00"
|
"revisionTime": "2016-02-29T08:42:30-08:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "mqxOM3CsubB09O0nDEe4efu0JLQ=",
|
"checksumSHA1": "EkdIh5Mk2bRiARtdoqUfnBuyndk=",
|
||||||
"path": "github.com/minio/minio-go",
|
"path": "github.com/minio/minio-go",
|
||||||
"revision": "414c6b6a2e97428776cd831d9745589ebcf873e5",
|
"revision": "9690dc6c40e6ef271727848c04f974e801212ac1",
|
||||||
"revisionTime": "2017-09-27T19:03:45Z"
|
"revisionTime": "2017-10-02T19:34:27Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "5juljGXPkBWENR2Os7dlnPQER48=",
|
"checksumSHA1": "5juljGXPkBWENR2Os7dlnPQER48=",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user