mirror of
https://github.com/minio/minio.git
synced 2025-11-09 21:49:46 -05:00
Add listparts support
This commit is contained in:
@@ -55,6 +55,29 @@ type ListObjectsResponse struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
// ListPartsResponse - format for list parts response
|
||||
type ListPartsResponse struct {
|
||||
XMLName xml.Name `xml:"http://doc.s3.amazonaws.com/2006-03-01 ListPartsResult" json:"-"`
|
||||
|
||||
Bucket string
|
||||
Key string
|
||||
UploadID string `xml:"UploadId"`
|
||||
|
||||
Initiator Initiator
|
||||
Owner Owner
|
||||
|
||||
// The class of storage used to store the object.
|
||||
StorageClass string
|
||||
|
||||
PartNumberMarker int
|
||||
NextPartNumberMarker int
|
||||
MaxParts int
|
||||
IsTruncated bool
|
||||
|
||||
// List of parts
|
||||
Part []*Part
|
||||
}
|
||||
|
||||
// ListBucketsResponse - format for list buckets response
|
||||
type ListBucketsResponse struct {
|
||||
XMLName xml.Name `xml:"http://doc.s3.amazonaws.com/2006-03-01 ListAllMyBucketsResult" json:"-"`
|
||||
@@ -76,6 +99,14 @@ type Bucket struct {
|
||||
CreationDate string
|
||||
}
|
||||
|
||||
// Part container for part metadata
|
||||
type Part struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
LastModified string
|
||||
Size int64
|
||||
}
|
||||
|
||||
// Object container for object metadata
|
||||
type Object struct {
|
||||
ETag string
|
||||
@@ -89,6 +120,9 @@ type Object struct {
|
||||
StorageClass string
|
||||
}
|
||||
|
||||
// Initiator inherit from Owner struct, fields are same
|
||||
type Initiator Owner
|
||||
|
||||
// Owner - bucket owner/principal
|
||||
type Owner struct {
|
||||
ID string
|
||||
@@ -126,12 +160,6 @@ type CompleteMultipartUploadResult struct {
|
||||
ETag string
|
||||
}
|
||||
|
||||
// Part description of a multipart part
|
||||
type Part struct {
|
||||
PartNumber int
|
||||
ETag string
|
||||
}
|
||||
|
||||
// List of not implemented bucket queries
|
||||
var notimplementedBucketResourceNames = map[string]bool{
|
||||
"policy": true,
|
||||
|
||||
@@ -345,6 +345,37 @@ func (server *minioAPI) putObjectPartHandler(w http.ResponseWriter, req *http.Re
|
||||
}
|
||||
}
|
||||
|
||||
func (server *minioAPI) listObjectPartsHandler(w http.ResponseWriter, req *http.Request) {
|
||||
acceptsContentType := getContentType(req)
|
||||
if acceptsContentType == unknownContentType {
|
||||
writeErrorResponse(w, req, NotAcceptable, acceptsContentType, req.URL.Path)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
uploadID := vars["uploadId"]
|
||||
|
||||
objectResourcesMetadata, err := server.driver.ListObjectParts(bucket, object, uploadID)
|
||||
switch err := iodine.ToError(err).(type) {
|
||||
case nil:
|
||||
response := generateListPartsResult(objectResourcesMetadata)
|
||||
encodedSuccessResponse := encodeSuccessResponse(response, acceptsContentType)
|
||||
// write headers
|
||||
setCommonHeaders(w, getContentTypeString(acceptsContentType))
|
||||
// set content-length to the size of the body
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(encodedSuccessResponse)))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
// write body
|
||||
w.Write(encodedSuccessResponse)
|
||||
case drivers.InvalidUploadID:
|
||||
writeErrorResponse(w, req, NoSuchUpload, acceptsContentType, req.URL.Path)
|
||||
default:
|
||||
log.Println(err)
|
||||
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func (server *minioAPI) completeMultipartUploadHandler(w http.ResponseWriter, req *http.Request) {
|
||||
acceptsContentType := getContentType(req)
|
||||
if acceptsContentType == unknownContentType {
|
||||
|
||||
@@ -112,6 +112,7 @@ func generateListObjectsResponse(bucket string, objects []drivers.ObjectMetadata
|
||||
return data
|
||||
}
|
||||
|
||||
// generateInitiateMultipartUploadResult
|
||||
func generateInitiateMultipartUploadResult(bucket, key, uploadID string) InitiateMultipartUploadResult {
|
||||
return InitiateMultipartUploadResult{
|
||||
Bucket: bucket,
|
||||
@@ -120,6 +121,7 @@ func generateInitiateMultipartUploadResult(bucket, key, uploadID string) Initiat
|
||||
}
|
||||
}
|
||||
|
||||
// generateCompleteMultipartUploadResult
|
||||
func generateCompleteMultpartUploadResult(bucket, key, location, etag string) CompleteMultipartUploadResult {
|
||||
return CompleteMultipartUploadResult{
|
||||
Location: location,
|
||||
@@ -129,13 +131,50 @@ func generateCompleteMultpartUploadResult(bucket, key, location, etag string) Co
|
||||
}
|
||||
}
|
||||
|
||||
// writeSuccessResponse - write success headers
|
||||
// partNumber
|
||||
type partNumber []*Part
|
||||
|
||||
func (b partNumber) Len() int { return len(b) }
|
||||
func (b partNumber) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b partNumber) Less(i, j int) bool { return b[i].PartNumber < b[j].PartNumber }
|
||||
|
||||
// generateListPartsResult
|
||||
func generateListPartsResult(objectMetadata drivers.ObjectResourcesMetadata) ListPartsResponse {
|
||||
// TODO - support EncodingType in xml decoding
|
||||
listPartsResponse := ListPartsResponse{}
|
||||
listPartsResponse.Bucket = objectMetadata.Bucket
|
||||
listPartsResponse.Key = objectMetadata.Key
|
||||
listPartsResponse.UploadID = objectMetadata.UploadID
|
||||
listPartsResponse.StorageClass = "STANDARD"
|
||||
listPartsResponse.Initiator.ID = "minio"
|
||||
listPartsResponse.Initiator.DisplayName = "minio"
|
||||
listPartsResponse.Owner.ID = "minio"
|
||||
listPartsResponse.Owner.DisplayName = "minio"
|
||||
|
||||
listPartsResponse.MaxParts = objectMetadata.MaxParts
|
||||
listPartsResponse.PartNumberMarker = objectMetadata.PartNumberMarker
|
||||
listPartsResponse.IsTruncated = objectMetadata.IsTruncated
|
||||
listPartsResponse.NextPartNumberMarker = objectMetadata.NextPartNumberMarker
|
||||
|
||||
listPartsResponse.Part = make([]*Part, len(objectMetadata.Part))
|
||||
for _, part := range objectMetadata.Part {
|
||||
newPart := &Part{}
|
||||
newPart.PartNumber = part.PartNumber
|
||||
newPart.ETag = part.ETag
|
||||
newPart.Size = part.Size
|
||||
newPart.LastModified = part.LastModified.Format(iso8601Format)
|
||||
listPartsResponse.Part = append(listPartsResponse.Part, newPart)
|
||||
}
|
||||
return listPartsResponse
|
||||
}
|
||||
|
||||
// writeSuccessResponse write success headers
|
||||
func writeSuccessResponse(w http.ResponseWriter, acceptsContentType contentType) {
|
||||
setCommonHeaders(w, getContentTypeString(acceptsContentType))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
// writeErrorRespone - write error headers
|
||||
// writeErrorRespone write error headers
|
||||
func writeErrorResponse(w http.ResponseWriter, req *http.Request, errorType int, acceptsContentType contentType, resource string) {
|
||||
error := getErrorCode(errorType)
|
||||
// generate error response
|
||||
|
||||
@@ -45,14 +45,16 @@ func HTTPHandler(driver drivers.Driver) http.Handler {
|
||||
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
|
||||
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
|
||||
mux.HandleFunc("/{bucket}", api.headBucketHandler).Methods("HEAD")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD")
|
||||
if featureflags.Get(featureflags.MultipartPutObject) {
|
||||
log.Println("Enabling feature", featureflags.MultipartPutObject)
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectPartHandler).Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectPartHandler).Queries("partNumber",
|
||||
"{partNumber:[0-9]+}", "uploadId", "{uploadId:.*}").Methods("PUT")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.listObjectPartsHandler).Queries("uploadId", "{uploadId:.*}").Methods("GET")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.completeMultipartUploadHandler).Queries("uploadId", "{uploadId:.*}").Methods("POST")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.newMultipartUploadHandler).Methods("POST")
|
||||
}
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT")
|
||||
|
||||
var conf = config.Config{}
|
||||
|
||||
@@ -42,6 +42,23 @@ func getBucketResources(values url.Values) (v drivers.BucketResourcesMetadata) {
|
||||
return
|
||||
}
|
||||
|
||||
// parse object url queries
|
||||
func getObjectResources(values url.Values) (v drivers.ObjectResourcesMetadata) {
|
||||
for key, value := range values {
|
||||
switch true {
|
||||
case key == "uploadId":
|
||||
v.UploadID = value[0]
|
||||
case key == "part-number-marker":
|
||||
v.PartNumberMarker, _ = strconv.Atoi(value[0])
|
||||
case key == "max-parts":
|
||||
v.MaxParts, _ = strconv.Atoi(value[0])
|
||||
case key == "encoding-type":
|
||||
v.EncodingType = value[0]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// check if req query values have acl
|
||||
func isRequestBucketACL(values url.Values) bool {
|
||||
for key := range values {
|
||||
|
||||
Reference in New Issue
Block a user