1
0
mirror of https://github.com/minio/mc.git synced 2025-11-13 12:22:45 +03:00
Files
mc/pkg/s3/object.go
Harshavardhana f21dc1a990 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
2015-03-08 18:47:45 -07:00

142 lines
3.4 KiB
Go

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)
}
}