oss/lib/storage_url.go (177 lines of code) (raw):
package lib
import (
"fmt"
"net/url"
"os"
"strings"
)
// SchemePrefix is the prefix of oss url
const SchemePrefix string = "oss://"
type CloudURLType string
const (
CloudURLNone CloudURLType = "none"
CloudURLService CloudURLType = "service"
CloudURLBucket CloudURLType = "bucket"
CloudURLObject CloudURLType = "object"
)
// StorageURLer is the interface for all url
type StorageURLer interface {
IsCloudURL() bool
IsFileURL() bool
ToString() string
}
// CloudURL describes oss url
type CloudURL struct {
urlStr string
bucket string
object string
}
// Init is used to create a cloud url from a user input url
func (cu *CloudURL) Init(urlStr, encodingType string) error {
cu.urlStr = urlStr
if err := cu.parseBucketObject(encodingType); err != nil {
return err
}
if err := cu.checkBucketObject(encodingType); err != nil {
return err
}
return nil
}
func (cu *CloudURL) parseBucketObject(encodingType string) error {
var err error
path := cu.urlStr
if strings.HasPrefix(strings.ToLower(path), SchemePrefix) {
path = string(path[len(SchemePrefix):])
} else {
// deal with the url: /bucket/object
if strings.HasPrefix(path, "/") {
path = string(path[1:])
}
}
sli := strings.SplitN(path, "/", 2)
cu.bucket = sli[0]
if len(sli) > 1 {
cu.object = sli[1]
if encodingType == URLEncodingType {
if cu.object, err = url.QueryUnescape(cu.object); err != nil {
return fmt.Errorf("invalid cloud url: %s, object name is not url encoded, %s", cu.urlStr, err.Error())
}
}
}
return nil
}
func (cu *CloudURL) checkBucketObject(encodingType string) error {
if cu.bucket == "" && cu.object != "" {
return fmt.Errorf("invalid cloud url: %s, miss bucket", cu.urlStr)
}
if encodingType == URLEncodingType && cu.bucket != "" && cu.object == "" {
if bucket, err := url.QueryUnescape(cu.bucket); err == nil && bucket != cu.bucket {
return fmt.Errorf("invalid cloud url: %s, bucket url do not support --encoding-type option", cu.urlStr)
}
}
return nil
}
func (cu *CloudURL) checkObjectPrefix() error {
if strings.HasPrefix(cu.object, "/") {
return fmt.Errorf("invalid cloud url: %s, object name should not begin with \"/\"", cu.urlStr)
}
if strings.HasPrefix(cu.object, "\\") {
return fmt.Errorf("invalid cloud url: %s, object name should not begin with \"\\\"", cu.urlStr)
}
return nil
}
func (cu *CloudURL) checkIsObjectURL() error {
if cu.bucket == "" {
return fmt.Errorf("invalid cloud url: %s, miss bucket", cu.urlStr)
}
if cu.object == "" {
return fmt.Errorf("invalid cloud url: %s, miss object", cu.urlStr)
}
return nil
}
// IsCloudURL shows if the url is a cloud url
func (cu CloudURL) IsCloudURL() bool {
return true
}
// IsFileURL shows if the url is a file url
func (cu CloudURL) IsFileURL() bool {
return false
}
// ToString reconstruct url
func (cu CloudURL) ToString() string {
if cu.object == "" {
return fmt.Sprintf("%s%s", SchemePrefix, cu.bucket)
}
return fmt.Sprintf("%s%s/%s", SchemePrefix, cu.bucket, cu.object)
}
// FileURL describes file url
type FileURL struct {
urlStr string
}
// Init simulate inheritance, and polymorphism
func (fu *FileURL) Init(urlStr, encodingType string) error {
if encodingType == URLEncodingType {
vurl, err := url.QueryUnescape(urlStr)
if err != nil {
return fmt.Errorf("invalid cloud url: %s, file name is not url encoded, %s", urlStr, err.Error())
}
urlStr = vurl
}
if len(urlStr) >= 2 && urlStr[:2] == "~"+string(os.PathSeparator) {
homeDir := currentHomeDir()
if homeDir != "" {
urlStr = strings.Replace(urlStr, "~", homeDir, 1)
} else {
return fmt.Errorf("current home dir is empty")
}
}
fu.urlStr = urlStr
return nil
}
// IsCloudURL simulate inheritance, and polymorphism
func (fu FileURL) IsCloudURL() bool {
return false
}
// IsFileURL simulate inheritance, and polymorphism
func (fu FileURL) IsFileURL() bool {
return true
}
// ToString simulate inheritance, and polymorphism
func (fu FileURL) ToString() string {
return fu.urlStr
}
// StorageURLFromString analysis input url type and build a storage url from the url
func StorageURLFromString(urlStr, encodingType string) (StorageURLer, error) {
if strings.HasPrefix(strings.ToLower(urlStr), SchemePrefix) {
var cloudURL CloudURL
if err := cloudURL.Init(urlStr, encodingType); err != nil {
return nil, err
}
return cloudURL, nil
}
var fileURL FileURL
if err := fileURL.Init(urlStr, encodingType); err != nil {
return nil, err
}
return fileURL, nil
}
// CloudURLFromString get a oss url from url, if url is not a cloud url, return error
func CloudURLFromString(urlStr, encodingType string) (CloudURL, error) {
storageURL, err := StorageURLFromString(urlStr, encodingType)
if err != nil {
return CloudURL{}, err
}
if !storageURL.IsCloudURL() {
return CloudURL{}, fmt.Errorf("invalid cloud url: \"%s\", please make sure the url starts with: \"%s\"", urlStr, SchemePrefix)
}
return storageURL.(CloudURL), nil
}
// ObjectURLFromString get a oss url from url, if url is not a cloud url, return error
func ObjectURLFromString(urlStr, encodingType string) (CloudURL, error) {
cloudURL, err := CloudURLFromString(urlStr, encodingType)
if err != nil {
return cloudURL, err
}
return cloudURL, cloudURL.checkIsObjectURL()
}
// CloudURLToString format url string from input
func CloudURLToString(bucket string, object string) string {
cloudURL := CloudURL{
bucket: bucket,
object: object,
}
return cloudURL.ToString()
}