// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn . //go:build plus package oss import ( "bytes" "errors" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ossconfigs" "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/iwind/TeaGo/types" "io" "net/http" ) type AliyunOSSProvider struct { bucket *oss.Bucket } func NewAliyunOSSProvider() *AliyunOSSProvider { return &AliyunOSSProvider{} } func (this *AliyunOSSProvider) Init(options ossconfigs.OSSOptions, bucketName string) error { realOptions, ok := options.(*ossconfigs.AliyunOSSProviderOptions) if !ok { return errors.New("invalid options for 'AliyunOSSProvider'") } client, err := oss.New(realOptions.Endpoint, realOptions.AccessKeyId, realOptions.AccessKeySecret) if err != nil { return err } bucket, err := client.Bucket(bucketName) if err != nil { return err } this.bucket = bucket return nil } func (this *AliyunOSSProvider) Head(key string) (httpResponse *http.Response, nativeErrCode string, err error) { header, respErr := this.bucket.GetObjectMeta(key) if err != nil { nativeErrCode, err = this.parseErr(respErr) return } // not found if len(header) == 0 { header = http.Header{"Content-Length": []string{"0"}} httpResponse = &http.Response{ StatusCode: http.StatusNotFound, Header: header, Body: io.NopCloser(bytes.NewReader(nil)), ContentLength: 0, } return } httpResponse = &http.Response{ StatusCode: http.StatusOK, Header: header, Body: io.NopCloser(bytes.NewReader(nil)), ContentLength: types.Int64(header.Get("Content-Length")), } return } func (this *AliyunOSSProvider) Get(key string) (httpResponse *http.Response, nativeErrCode string, err error) { body, respErr := this.bucket.GetObject(key) if respErr != nil { nativeErrCode, err = this.parseErr(respErr) return } resp, ok := body.(*oss.Response) if ok { httpResponse = &http.Response{ StatusCode: http.StatusOK, Header: resp.Headers, Body: resp.Body, ContentLength: types.Int64(resp.Headers.Get("Content-Length")), } return } httpResponse = &http.Response{ StatusCode: http.StatusOK, Body: body, } return } func (this *AliyunOSSProvider) GetRange(key string, bytesRange string) (httpResponse *http.Response, nativeErrCode string, err error) { rangeResult, parseOk := httpRequestParseRangeHeader(bytesRange) if !parseOk || len(rangeResult) == 0 { return this.Get(key) } var firstRange = rangeResult[0] var start = firstRange.Start() var end = firstRange.End() if start < 0 || end < 0 || start > end { return this.Get(key) } body, respErr := this.bucket.GetObject(key, oss.Range(start, end)) if respErr != nil { nativeErrCode, err = this.parseErr(respErr) return } resp, ok := body.(*oss.Response) if ok { httpResponse = &http.Response{ StatusCode: resp.StatusCode, Header: resp.Headers, Body: resp.Body, ContentLength: types.Int64(resp.Headers.Get("Content-Length")), } return } httpResponse = &http.Response{ StatusCode: http.StatusPartialContent, Body: body, } return } func (this *AliyunOSSProvider) parseErr(err error) (errCode string, resultErr error) { if err == nil { return } resultErr = err serviceErr, ok := err.(oss.ServiceError) if ok { errCode = serviceErr.Code // 特殊错误 if errCode == "NoSuchBucket" { resultErr = errNoBucket return } if errCode == "NoSuchKey" { resultErr = errNotFound } if errCode == "RequestTimeout" { resultErr = errRequestTimeout return } } return }