137 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
| package swift
 | |
| 
 | |
| import (
 | |
| 	"os"
 | |
| )
 | |
| 
 | |
| // DynamicLargeObjectCreateFile represents an open static large object
 | |
| type DynamicLargeObjectCreateFile struct {
 | |
| 	largeObjectCreateFile
 | |
| }
 | |
| 
 | |
| // DynamicLargeObjectCreateFile creates a dynamic large object
 | |
| // returning an object which satisfies io.Writer, io.Seeker, io.Closer
 | |
| // and io.ReaderFrom.  The flags are as passes to the
 | |
| // largeObjectCreate method.
 | |
| func (c *Connection) DynamicLargeObjectCreateFile(opts *LargeObjectOpts) (LargeObjectFile, error) {
 | |
| 	lo, err := c.largeObjectCreate(opts)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return withBuffer(opts, &DynamicLargeObjectCreateFile{
 | |
| 		largeObjectCreateFile: *lo,
 | |
| 	}), nil
 | |
| }
 | |
| 
 | |
| // DynamicLargeObjectCreate creates or truncates an existing dynamic
 | |
| // large object returning a writeable object.  This sets opts.Flags to
 | |
| // an appropriate value before calling DynamicLargeObjectCreateFile
 | |
| func (c *Connection) DynamicLargeObjectCreate(opts *LargeObjectOpts) (LargeObjectFile, error) {
 | |
| 	opts.Flags = os.O_TRUNC | os.O_CREATE
 | |
| 	return c.DynamicLargeObjectCreateFile(opts)
 | |
| }
 | |
| 
 | |
| // DynamicLargeObjectDelete deletes a dynamic large object and all of its segments.
 | |
| func (c *Connection) DynamicLargeObjectDelete(container string, path string) error {
 | |
| 	return c.LargeObjectDelete(container, path)
 | |
| }
 | |
| 
 | |
| // DynamicLargeObjectMove moves a dynamic large object from srcContainer, srcObjectName to dstContainer, dstObjectName
 | |
| func (c *Connection) DynamicLargeObjectMove(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error {
 | |
| 	info, headers, err := c.Object(dstContainer, srcObjectName)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	segmentContainer, segmentPath := parseFullPath(headers["X-Object-Manifest"])
 | |
| 	if err := c.createDLOManifest(dstContainer, dstObjectName, segmentContainer+"/"+segmentPath, info.ContentType); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := c.ObjectDelete(srcContainer, srcObjectName); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // createDLOManifest creates a dynamic large object manifest
 | |
| func (c *Connection) createDLOManifest(container string, objectName string, prefix string, contentType string) error {
 | |
| 	headers := make(Headers)
 | |
| 	headers["X-Object-Manifest"] = prefix
 | |
| 	manifest, err := c.ObjectCreate(container, objectName, false, "", contentType, headers)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err := manifest.Close(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Close satisfies the io.Closer interface
 | |
| func (file *DynamicLargeObjectCreateFile) Close() error {
 | |
| 	return file.Flush()
 | |
| }
 | |
| 
 | |
| func (file *DynamicLargeObjectCreateFile) Flush() error {
 | |
| 	err := file.conn.createDLOManifest(file.container, file.objectName, file.segmentContainer+"/"+file.prefix, file.contentType)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return file.conn.waitForSegmentsToShowUp(file.container, file.objectName, file.Size())
 | |
| }
 | |
| 
 | |
| func (c *Connection) getAllDLOSegments(segmentContainer, segmentPath string) ([]Object, error) {
 | |
| 	//a simple container listing works 99.9% of the time
 | |
| 	segments, err := c.ObjectsAll(segmentContainer, &ObjectsOpts{Prefix: segmentPath})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	hasObjectName := make(map[string]struct{})
 | |
| 	for _, segment := range segments {
 | |
| 		hasObjectName[segment.Name] = struct{}{}
 | |
| 	}
 | |
| 
 | |
| 	//The container listing might be outdated (i.e. not contain all existing
 | |
| 	//segment objects yet) because of temporary inconsistency (Swift is only
 | |
| 	//eventually consistent!). Check its completeness.
 | |
| 	segmentNumber := 0
 | |
| 	for {
 | |
| 		segmentNumber++
 | |
| 		segmentName := getSegment(segmentPath, segmentNumber)
 | |
| 		if _, seen := hasObjectName[segmentName]; seen {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		//This segment is missing in the container listing. Use a more reliable
 | |
| 		//request to check its existence. (HEAD requests on segments are
 | |
| 		//guaranteed to return the correct metadata, except for the pathological
 | |
| 		//case of an outage of large parts of the Swift cluster or its network,
 | |
| 		//since every segment is only written once.)
 | |
| 		segment, _, err := c.Object(segmentContainer, segmentName)
 | |
| 		switch err {
 | |
| 		case nil:
 | |
| 			//found new segment -> add it in the correct position and keep
 | |
| 			//going, more might be missing
 | |
| 			if segmentNumber <= len(segments) {
 | |
| 				segments = append(segments[:segmentNumber], segments[segmentNumber-1:]...)
 | |
| 				segments[segmentNumber-1] = segment
 | |
| 			} else {
 | |
| 				segments = append(segments, segment)
 | |
| 			}
 | |
| 			continue
 | |
| 		case ObjectNotFound:
 | |
| 			//This segment is missing. Since we upload segments sequentially,
 | |
| 			//there won't be any more segments after it.
 | |
| 			return segments, nil
 | |
| 		default:
 | |
| 			return nil, err //unexpected error
 | |
| 		}
 | |
| 	}
 | |
| }
 |