123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933 |
- package oss
- import (
- "bytes"
- "crypto/md5"
- "encoding/base64"
- "encoding/xml"
- "fmt"
- "hash"
- "hash/crc64"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "os"
- "strconv"
- "time"
- )
- type Bucket struct {
- Client Client
- BucketName string
- }
- func (bucket Bucket) PutObject(objectKey string, reader io.Reader, options ...Option) error {
- opts := addContentType(options, objectKey)
- request := &PutObjectRequest{
- ObjectKey: objectKey,
- Reader: reader,
- }
- resp, err := bucket.DoPutObject(request, opts)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return err
- }
- func (bucket Bucket) PutObjectFromFile(objectKey, filePath string, options ...Option) error {
- fd, err := os.Open(filePath)
- if err != nil {
- return err
- }
- defer fd.Close()
- opts := addContentType(options, filePath, objectKey)
- request := &PutObjectRequest{
- ObjectKey: objectKey,
- Reader: fd,
- }
- resp, err := bucket.DoPutObject(request, opts)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return err
- }
- func (bucket Bucket) DoPutObject(request *PutObjectRequest, options []Option) (*Response, error) {
- isOptSet, _, _ := isOptionSet(options, HTTPHeaderContentType)
- if !isOptSet {
- options = addContentType(options, request.ObjectKey)
- }
- listener := getProgressListener(options)
- params := map[string]interface{}{}
- resp, err := bucket.do("PUT", request.ObjectKey, params, options, request.Reader, listener)
- if err != nil {
- return nil, err
- }
- if bucket.getConfig().IsEnableCRC {
- err = checkCRC(resp, "DoPutObject")
- if err != nil {
- return resp, err
- }
- }
- err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
- return resp, err
- }
- func (bucket Bucket) GetObject(objectKey string, options ...Option) (io.ReadCloser, error) {
- result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
- if err != nil {
- return nil, err
- }
- return result.Response.Body, nil
- }
- func (bucket Bucket) GetObjectToFile(objectKey, filePath string, options ...Option) error {
- tempFilePath := filePath + TempFileSuffix
-
- result, err := bucket.DoGetObject(&GetObjectRequest{objectKey}, options)
- if err != nil {
- return err
- }
- defer result.Response.Body.Close()
-
- fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
- if err != nil {
- return err
- }
-
- _, err = io.Copy(fd, result.Response.Body)
- fd.Close()
- if err != nil {
- return err
- }
-
- hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
- if bucket.getConfig().IsEnableCRC && !hasRange {
- result.Response.ClientCRC = result.ClientCRC.Sum64()
- err = checkCRC(result.Response, "GetObjectToFile")
- if err != nil {
- os.Remove(tempFilePath)
- return err
- }
- }
- return os.Rename(tempFilePath, filePath)
- }
- func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*GetObjectResult, error) {
- params := map[string]interface{}{}
- resp, err := bucket.do("GET", request.ObjectKey, params, options, nil, nil)
- if err != nil {
- return nil, err
- }
- result := &GetObjectResult{
- Response: resp,
- }
-
- var crcCalc hash.Hash64
- hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
- if bucket.getConfig().IsEnableCRC && !hasRange {
- crcCalc = crc64.New(crcTable())
- result.ServerCRC = resp.ServerCRC
- result.ClientCRC = crcCalc
- }
-
- listener := getProgressListener(options)
- contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
- resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
- return result, nil
- }
- func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
- var out CopyObjectResult
- options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
- params := map[string]interface{}{}
- resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
- if err != nil {
- return out, err
- }
- defer resp.Body.Close()
- err = xmlUnmarshal(resp.Body, &out)
- return out, err
- }
- func (bucket Bucket) CopyObjectTo(destBucketName, destObjectKey, srcObjectKey string, options ...Option) (CopyObjectResult, error) {
- return bucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
- }
- func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
- destBucketName := bucket.BucketName
- var out CopyObjectResult
- srcBucket, err := bucket.Client.Bucket(srcBucketName)
- if err != nil {
- return out, err
- }
- return srcBucket.copy(srcObjectKey, destBucketName, destObjectKey, options...)
- }
- func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
- var out CopyObjectResult
- options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
- headers := make(map[string]string)
- err := handleOptions(headers, options)
- if err != nil {
- return out, err
- }
- params := map[string]interface{}{}
- resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)
- if err != nil {
- return out, err
- }
- defer resp.Body.Close()
- err = xmlUnmarshal(resp.Body, &out)
- return out, err
- }
- func (bucket Bucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...Option) (int64, error) {
- request := &AppendObjectRequest{
- ObjectKey: objectKey,
- Reader: reader,
- Position: appendPosition,
- }
- result, err := bucket.DoAppendObject(request, options)
- if err != nil {
- return appendPosition, err
- }
- return result.NextPosition, err
- }
- func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Option) (*AppendObjectResult, error) {
- params := map[string]interface{}{}
- params["append"] = nil
- params["position"] = strconv.FormatInt(request.Position, 10)
- headers := make(map[string]string)
- opts := addContentType(options, request.ObjectKey)
- handleOptions(headers, opts)
- var initCRC uint64
- isCRCSet, initCRCOpt, _ := isOptionSet(options, initCRC64)
- if isCRCSet {
- initCRC = initCRCOpt.(uint64)
- }
- listener := getProgressListener(options)
- handleOptions(headers, opts)
- resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
- request.Reader, initCRC, listener)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- nextPosition, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderOssNextAppendPosition), 10, 64)
- result := &AppendObjectResult{
- NextPosition: nextPosition,
- CRC: resp.ServerCRC,
- }
- if bucket.getConfig().IsEnableCRC && isCRCSet {
- err = checkCRC(resp, "AppendObject")
- if err != nil {
- return result, err
- }
- }
- return result, nil
- }
- func (bucket Bucket) DeleteObject(objectKey string) error {
- params := map[string]interface{}{}
- resp, err := bucket.do("DELETE", objectKey, params, nil, nil, nil)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return checkRespCode(resp.StatusCode, []int{http.StatusNoContent})
- }
- func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (DeleteObjectsResult, error) {
- out := DeleteObjectsResult{}
- dxml := deleteXML{}
- for _, key := range objectKeys {
- dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
- }
- isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
- dxml.Quiet = isQuiet.(bool)
- bs, err := xml.Marshal(dxml)
- if err != nil {
- return out, err
- }
- buffer := new(bytes.Buffer)
- buffer.Write(bs)
- contentType := http.DetectContentType(buffer.Bytes())
- options = append(options, ContentType(contentType))
- sum := md5.Sum(bs)
- b64 := base64.StdEncoding.EncodeToString(sum[:])
- options = append(options, ContentMD5(b64))
- params := map[string]interface{}{}
- params["delete"] = nil
- params["encoding-type"] = "url"
- resp, err := bucket.do("POST", "", params, options, buffer, nil)
- if err != nil {
- return out, err
- }
- defer resp.Body.Close()
- if !dxml.Quiet {
- if err = xmlUnmarshal(resp.Body, &out); err == nil {
- err = decodeDeleteObjectsResult(&out)
- }
- }
- return out, err
- }
- func (bucket Bucket) IsObjectExist(objectKey string) (bool, error) {
- _, err := bucket.GetObjectMeta(objectKey)
- if err == nil {
- return true, nil
- }
- switch err.(type) {
- case ServiceError:
- if err.(ServiceError).StatusCode == 404 && err.(ServiceError).Code == "NoSuchKey" {
- return false, nil
- }
- }
- return false, err
- }
- func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
- var out ListObjectsResult
- options = append(options, EncodingType("url"))
- params, err := getRawParams(options)
- if err != nil {
- return out, err
- }
- resp, err := bucket.do("GET", "", params, nil, nil, nil)
- if err != nil {
- return out, err
- }
- defer resp.Body.Close()
- err = xmlUnmarshal(resp.Body, &out)
- if err != nil {
- return out, err
- }
- err = decodeListObjectsResult(&out)
- return out, err
- }
- func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
- options = append(options, MetadataDirective(MetaReplace))
- _, err := bucket.CopyObject(objectKey, objectKey, options...)
- return err
- }
- func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
- params := map[string]interface{}{}
- resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- return resp.Headers, nil
- }
- func (bucket Bucket) GetObjectMeta(objectKey string) (http.Header, error) {
- params := map[string]interface{}{}
- params["objectMeta"] = nil
-
- resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- return resp.Headers, nil
- }
- func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
- options := []Option{ObjectACL(objectACL)}
- params := map[string]interface{}{}
- params["acl"] = nil
- resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return checkRespCode(resp.StatusCode, []int{http.StatusOK})
- }
- func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error) {
- var out GetObjectACLResult
- params := map[string]interface{}{}
- params["acl"] = nil
- resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
- if err != nil {
- return out, err
- }
- defer resp.Body.Close()
- err = xmlUnmarshal(resp.Body, &out)
- return out, err
- }
- func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
- options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
- params := map[string]interface{}{}
- params["symlink"] = nil
- resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return checkRespCode(resp.StatusCode, []int{http.StatusOK})
- }
- func (bucket Bucket) GetSymlink(objectKey string) (http.Header, error) {
- params := map[string]interface{}{}
- params["symlink"] = nil
- resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- targetObjectKey := resp.Headers.Get(HTTPHeaderOssSymlinkTarget)
- targetObjectKey, err = url.QueryUnescape(targetObjectKey)
- if err != nil {
- return resp.Headers, err
- }
- resp.Headers.Set(HTTPHeaderOssSymlinkTarget, targetObjectKey)
- return resp.Headers, err
- }
- func (bucket Bucket) RestoreObject(objectKey string) error {
- params := map[string]interface{}{}
- params["restore"] = nil
- resp, err := bucket.do("POST", objectKey, params, nil, nil, nil)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return checkRespCode(resp.StatusCode, []int{http.StatusOK, http.StatusAccepted})
- }
- func (bucket Bucket) SignURL(objectKey string, method HTTPMethod, expiredInSec int64, options ...Option) (string, error) {
- if expiredInSec < 0 {
- return "", fmt.Errorf("invalid expires: %d, expires must bigger than 0", expiredInSec)
- }
- expiration := time.Now().Unix() + expiredInSec
- params, err := getRawParams(options)
- if err != nil {
- return "", err
- }
- headers := make(map[string]string)
- err = handleOptions(headers, options)
- if err != nil {
- return "", err
- }
- return bucket.Client.Conn.signURL(method, bucket.BucketName, objectKey, expiration, params, headers), nil
- }
- func (bucket Bucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...Option) error {
- resp, err := bucket.DoPutObjectWithURL(signedURL, reader, options)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return err
- }
- func (bucket Bucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...Option) error {
- fd, err := os.Open(filePath)
- if err != nil {
- return err
- }
- defer fd.Close()
- resp, err := bucket.DoPutObjectWithURL(signedURL, fd, options)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- return err
- }
- func (bucket Bucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []Option) (*Response, error) {
- listener := getProgressListener(options)
- params := map[string]interface{}{}
- resp, err := bucket.doURL("PUT", signedURL, params, options, reader, listener)
- if err != nil {
- return nil, err
- }
- if bucket.getConfig().IsEnableCRC {
- err = checkCRC(resp, "DoPutObjectWithURL")
- if err != nil {
- return resp, err
- }
- }
- err = checkRespCode(resp.StatusCode, []int{http.StatusOK})
- return resp, err
- }
- func (bucket Bucket) GetObjectWithURL(signedURL string, options ...Option) (io.ReadCloser, error) {
- result, err := bucket.DoGetObjectWithURL(signedURL, options)
- if err != nil {
- return nil, err
- }
- return result.Response.Body, nil
- }
- func (bucket Bucket) GetObjectToFileWithURL(signedURL, filePath string, options ...Option) error {
- tempFilePath := filePath + TempFileSuffix
-
- result, err := bucket.DoGetObjectWithURL(signedURL, options)
- if err != nil {
- return err
- }
- defer result.Response.Body.Close()
-
- fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, FilePermMode)
- if err != nil {
- return err
- }
-
- _, err = io.Copy(fd, result.Response.Body)
- fd.Close()
- if err != nil {
- return err
- }
-
- hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
- if bucket.getConfig().IsEnableCRC && !hasRange {
- result.Response.ClientCRC = result.ClientCRC.Sum64()
- err = checkCRC(result.Response, "GetObjectToFileWithURL")
- if err != nil {
- os.Remove(tempFilePath)
- return err
- }
- }
- return os.Rename(tempFilePath, filePath)
- }
- func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*GetObjectResult, error) {
- params := map[string]interface{}{}
- resp, err := bucket.doURL("GET", signedURL, params, options, nil, nil)
- if err != nil {
- return nil, err
- }
- result := &GetObjectResult{
- Response: resp,
- }
-
- var crcCalc hash.Hash64
- hasRange, _, _ := isOptionSet(options, HTTPHeaderRange)
- if bucket.getConfig().IsEnableCRC && !hasRange {
- crcCalc = crc64.New(crcTable())
- result.ServerCRC = resp.ServerCRC
- result.ClientCRC = crcCalc
- }
-
- listener := getProgressListener(options)
- contentLen, _ := strconv.ParseInt(resp.Headers.Get(HTTPHeaderContentLength), 10, 64)
- resp.Body = ioutil.NopCloser(TeeReader(resp.Body, crcCalc, contentLen, listener, nil))
- return result, nil
- }
- func (bucket Bucket) do(method, objectName string, params map[string]interface{}, options []Option,
- data io.Reader, listener ProgressListener) (*Response, error) {
- headers := make(map[string]string)
- err := handleOptions(headers, options)
- if err != nil {
- return nil, err
- }
- return bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
- params, headers, data, 0, listener)
- }
- func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[string]interface{}, options []Option,
- data io.Reader, listener ProgressListener) (*Response, error) {
- headers := make(map[string]string)
- err := handleOptions(headers, options)
- if err != nil {
- return nil, err
- }
- return bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)
- }
- func (bucket Bucket) getConfig() *Config {
- return bucket.Client.Config
- }
- func addContentType(options []Option, keys ...string) []Option {
- typ := TypeByExtension("")
- for _, key := range keys {
- typ = TypeByExtension(key)
- if typ != "" {
- break
- }
- }
- if typ == "" {
- typ = "application/octet-stream"
- }
- opts := []Option{ContentType(typ)}
- opts = append(opts, options...)
- return opts
- }
|