mirror of
https://github.com/minio/mc.git
synced 2025-11-13 12:22:45 +03:00
Code cleanup and simplification
- Print in human-readable not amazon format
- Send stat() before accessing object, error gracefully
- Move client and bucket API operations to their own files
- Remove unused functions, constants and variables
- Rename API operations to sensible names
* GetBucket --> ListObjects
* Buckets --> ListBuckets
This commit is contained in:
141
pkg/s3/object.go
Normal file
141
pkg/s3/object.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
/// Object API operations
|
||||
|
||||
// Put - upload new object to bucket
|
||||
func (c *Client) Put(bucket, key string, size int64, contents io.Reader) error {
|
||||
req := newReq(c.keyURL(bucket, key))
|
||||
req.Method = "PUT"
|
||||
req.ContentLength = size
|
||||
|
||||
h := md5.New()
|
||||
// Memory where data is present
|
||||
sink := new(bytes.Buffer)
|
||||
mw := io.MultiWriter(h, sink)
|
||||
written, err := io.Copy(mw, contents)
|
||||
if written != size {
|
||||
return fmt.Errorf("Data read mismatch")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Body = ioutil.NopCloser(sink)
|
||||
b64 := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
req.Header.Set("Content-MD5", b64)
|
||||
c.Auth.signRequest(req)
|
||||
|
||||
res, err := c.transport().RoundTrip(req)
|
||||
if res != nil && res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
// res.Write(os.Stderr)
|
||||
return fmt.Errorf("Got response code %d from s3", res.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat - returns 0, "", os.ErrNotExist if not on S3
|
||||
func (c *Client) Stat(key, bucket string) (size int64, date time.Time, reterr error) {
|
||||
req := newReq(c.keyURL(bucket, key))
|
||||
req.Method = "HEAD"
|
||||
c.Auth.signRequest(req)
|
||||
res, err := c.transport().RoundTrip(req)
|
||||
if res != nil && res.Body != nil {
|
||||
defer res.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return 0, date, err
|
||||
}
|
||||
|
||||
switch res.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return 0, date, os.ErrNotExist
|
||||
case http.StatusOK:
|
||||
size, err = strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
|
||||
if err != nil {
|
||||
return 0, date, err
|
||||
}
|
||||
if dateStr := res.Header.Get("Last-Modified"); dateStr != "" {
|
||||
// AWS S3 uses RFC1123 standard for Date in HTTP header, unlike XML content
|
||||
date, err := time.Parse(time.RFC1123, dateStr)
|
||||
if err != nil {
|
||||
return 0, date, err
|
||||
}
|
||||
return size, date, nil
|
||||
}
|
||||
default:
|
||||
return 0, date, fmt.Errorf("s3: Unexpected status code %d statting object %v", res.StatusCode, key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get - download a requested object from a given bucket
|
||||
func (c *Client) Get(bucket, key string) (body io.ReadCloser, size int64, err error) {
|
||||
req := newReq(c.keyURL(bucket, key))
|
||||
c.Auth.signRequest(req)
|
||||
res, err := c.transport().RoundTrip(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
return res.Body, res.ContentLength, nil
|
||||
case http.StatusNotFound:
|
||||
res.Body.Close()
|
||||
return nil, 0, os.ErrNotExist
|
||||
default:
|
||||
res.Body.Close()
|
||||
return nil, 0, fmt.Errorf("Amazon HTTP error on GET: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// GetPartial fetches part of the s3 key object in bucket.
|
||||
// If length is negative, the rest of the object is returned.
|
||||
// The caller must close rc.
|
||||
func (c *Client) GetPartial(bucket, key string, offset, length int64) (rc io.ReadCloser, err error) {
|
||||
if offset < 0 {
|
||||
return nil, fmt.Errorf("invalid negative length")
|
||||
}
|
||||
|
||||
req := newReq(c.keyURL(bucket, key))
|
||||
if length >= 0 {
|
||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1))
|
||||
} else {
|
||||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
|
||||
}
|
||||
c.Auth.signRequest(req)
|
||||
|
||||
res, err := c.transport().RoundTrip(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK, http.StatusPartialContent:
|
||||
return res.Body, nil
|
||||
case http.StatusNotFound:
|
||||
res.Body.Close()
|
||||
return nil, os.ErrNotExist
|
||||
default:
|
||||
res.Body.Close()
|
||||
return nil, fmt.Errorf("Amazon HTTP error on GET: %d", res.StatusCode)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user