2020-07-03 13:52:04 +00:00
|
|
|
package layer
|
|
|
|
|
|
|
|
import (
|
2020-08-03 11:48:33 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2020-10-24 13:09:22 +00:00
|
|
|
"strconv"
|
2020-07-03 13:52:04 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2020-10-19 01:04:37 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
|
|
|
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
2020-07-03 13:52:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2020-08-03 11:48:33 +00:00
|
|
|
ObjectInfo struct {
|
2021-01-14 17:39:48 +00:00
|
|
|
id *object.ID
|
|
|
|
isDir bool
|
2020-10-24 13:09:22 +00:00
|
|
|
|
2020-08-03 11:48:33 +00:00
|
|
|
Bucket string
|
|
|
|
Name string
|
|
|
|
Size int64
|
|
|
|
ContentType string
|
|
|
|
Created time.Time
|
2020-10-19 01:04:37 +00:00
|
|
|
Owner *owner.ID
|
2020-08-03 11:48:33 +00:00
|
|
|
Headers map[string]string
|
2020-07-03 13:52:04 +00:00
|
|
|
}
|
|
|
|
|
2020-08-03 11:48:33 +00:00
|
|
|
// ListObjectsInfo - container for list objects.
|
|
|
|
ListObjectsInfo struct {
|
|
|
|
// Indicates whether the returned list objects response is truncated. A
|
|
|
|
// value of true indicates that the list was truncated. The list can be truncated
|
|
|
|
// if the number of objects exceeds the limit allowed or specified
|
|
|
|
// by max keys.
|
|
|
|
IsTruncated bool
|
|
|
|
|
|
|
|
// When response is truncated (the IsTruncated element value in the response
|
|
|
|
// is true), you can use the key name in this field as marker in the subsequent
|
|
|
|
// request to get next set of objects.
|
|
|
|
//
|
|
|
|
// NOTE: This element is returned only if you have delimiter request parameter
|
|
|
|
// specified.
|
|
|
|
ContinuationToken string
|
|
|
|
NextContinuationToken string
|
|
|
|
|
|
|
|
// List of objects info for this request.
|
2020-10-19 01:04:37 +00:00
|
|
|
Objects []*ObjectInfo
|
2020-08-03 11:48:33 +00:00
|
|
|
|
|
|
|
// List of prefixes for this request.
|
|
|
|
Prefixes []string
|
2020-07-03 13:52:04 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2021-01-14 17:39:48 +00:00
|
|
|
const (
|
|
|
|
rootSeparator = "root://"
|
|
|
|
PathSeparator = string(os.PathSeparator)
|
|
|
|
)
|
2020-07-03 13:52:04 +00:00
|
|
|
|
2020-10-19 01:04:37 +00:00
|
|
|
func userHeaders(attrs []*object.Attribute) map[string]string {
|
|
|
|
result := make(map[string]string, len(attrs))
|
2020-07-03 13:52:04 +00:00
|
|
|
|
2020-10-19 01:04:37 +00:00
|
|
|
for _, attr := range attrs {
|
2020-11-24 07:01:38 +00:00
|
|
|
result[attr.Key()] = attr.Value()
|
2020-07-03 13:52:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2021-01-14 17:39:48 +00:00
|
|
|
func objectInfoFromMeta(bkt *BucketInfo, meta *object.Object, prefix string) *ObjectInfo {
|
2020-10-24 13:09:22 +00:00
|
|
|
var (
|
2021-01-14 17:39:48 +00:00
|
|
|
isDir bool
|
|
|
|
size int64
|
|
|
|
mimeType string
|
|
|
|
creation time.Time
|
|
|
|
filename = meta.ID().String()
|
|
|
|
name, dirname = nameFromObject(meta)
|
2020-10-24 13:09:22 +00:00
|
|
|
)
|
2020-07-03 13:52:04 +00:00
|
|
|
|
2021-01-14 17:39:48 +00:00
|
|
|
if !strings.HasPrefix(dirname, prefix) && prefix != rootSeparator {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if ln := len(prefix); ln > 0 && prefix[ln-1:] != PathSeparator {
|
|
|
|
prefix += PathSeparator
|
|
|
|
}
|
|
|
|
|
2020-11-24 07:01:38 +00:00
|
|
|
userHeaders := userHeaders(meta.Attributes())
|
2020-10-24 13:09:22 +00:00
|
|
|
if val, ok := userHeaders[object.AttributeFileName]; ok {
|
|
|
|
filename = val
|
|
|
|
delete(userHeaders, object.AttributeFileName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if val, ok := userHeaders[object.AttributeTimestamp]; !ok {
|
|
|
|
// ignore empty value
|
|
|
|
} else if dt, err := strconv.ParseInt(val, 10, 64); err == nil {
|
|
|
|
creation = time.Unix(dt, 0)
|
|
|
|
delete(userHeaders, object.AttributeTimestamp)
|
2020-07-03 13:52:04 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 17:39:48 +00:00
|
|
|
tail := strings.TrimPrefix(dirname, prefix)
|
|
|
|
index := strings.Index(tail, PathSeparator)
|
|
|
|
|
|
|
|
if prefix == rootSeparator {
|
|
|
|
size = int64(meta.PayloadSize())
|
|
|
|
mimeType = http.DetectContentType(meta.Payload())
|
|
|
|
} else if index < 0 {
|
|
|
|
filename = name
|
|
|
|
size = int64(meta.PayloadSize())
|
|
|
|
mimeType = http.DetectContentType(meta.Payload())
|
|
|
|
} else {
|
|
|
|
isDir = true
|
|
|
|
filename = tail[:index] + PathSeparator
|
|
|
|
userHeaders = nil
|
|
|
|
}
|
2020-08-03 11:48:33 +00:00
|
|
|
|
|
|
|
return &ObjectInfo{
|
2021-01-14 17:39:48 +00:00
|
|
|
id: meta.ID(),
|
|
|
|
isDir: isDir,
|
2020-10-24 13:09:22 +00:00
|
|
|
|
|
|
|
Bucket: bkt.Name,
|
|
|
|
Name: filename,
|
|
|
|
Created: creation,
|
2020-08-03 11:48:33 +00:00
|
|
|
ContentType: mimeType,
|
|
|
|
Headers: userHeaders,
|
2021-01-14 17:39:48 +00:00
|
|
|
Owner: meta.OwnerID(),
|
|
|
|
Size: size,
|
2020-07-03 13:52:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-19 01:04:37 +00:00
|
|
|
func nameFromObject(o *object.Object) (string, string) {
|
2020-11-24 07:01:38 +00:00
|
|
|
var name = o.ID().String()
|
2020-07-03 13:52:04 +00:00
|
|
|
|
2020-11-24 07:01:38 +00:00
|
|
|
for _, attr := range o.Attributes() {
|
|
|
|
if attr.Key() == object.AttributeFileName {
|
|
|
|
name = attr.Value()
|
2020-07-03 13:52:04 +00:00
|
|
|
|
2020-10-19 01:04:37 +00:00
|
|
|
break
|
|
|
|
}
|
2020-07-03 13:52:04 +00:00
|
|
|
}
|
|
|
|
|
2021-01-14 17:39:48 +00:00
|
|
|
return NameFromString(name)
|
|
|
|
}
|
2020-07-03 13:52:04 +00:00
|
|
|
|
2021-01-14 17:39:48 +00:00
|
|
|
func NameFromString(name string) (string, string) {
|
|
|
|
ind := strings.LastIndex(name, PathSeparator)
|
2020-07-03 13:52:04 +00:00
|
|
|
return name[ind+1:], name[:ind+1]
|
|
|
|
}
|
2020-10-24 13:09:22 +00:00
|
|
|
|
|
|
|
func (o *ObjectInfo) ID() *object.ID { return o.id }
|
2021-01-14 17:39:48 +00:00
|
|
|
|
|
|
|
func (o *ObjectInfo) IsDir() bool { return o.isDir }
|