1package swift 2 3import ( 4 "os" 5) 6 7// DynamicLargeObjectCreateFile represents an open static large object 8type DynamicLargeObjectCreateFile struct { 9 largeObjectCreateFile 10} 11 12// DynamicLargeObjectCreateFile creates a dynamic large object 13// returning an object which satisfies io.Writer, io.Seeker, io.Closer 14// and io.ReaderFrom. The flags are as passes to the 15// largeObjectCreate method. 16func (c *Connection) DynamicLargeObjectCreateFile(opts *LargeObjectOpts) (LargeObjectFile, error) { 17 lo, err := c.largeObjectCreate(opts) 18 if err != nil { 19 return nil, err 20 } 21 22 return withBuffer(opts, &DynamicLargeObjectCreateFile{ 23 largeObjectCreateFile: *lo, 24 }), nil 25} 26 27// DynamicLargeObjectCreate creates or truncates an existing dynamic 28// large object returning a writeable object. This sets opts.Flags to 29// an appropriate value before calling DynamicLargeObjectCreateFile 30func (c *Connection) DynamicLargeObjectCreate(opts *LargeObjectOpts) (LargeObjectFile, error) { 31 opts.Flags = os.O_TRUNC | os.O_CREATE 32 return c.DynamicLargeObjectCreateFile(opts) 33} 34 35// DynamicLargeObjectDelete deletes a dynamic large object and all of its segments. 36func (c *Connection) DynamicLargeObjectDelete(container string, path string) error { 37 return c.LargeObjectDelete(container, path) 38} 39 40// DynamicLargeObjectMove moves a dynamic large object from srcContainer, srcObjectName to dstContainer, dstObjectName 41func (c *Connection) DynamicLargeObjectMove(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error { 42 info, headers, err := c.Object(dstContainer, srcObjectName) 43 if err != nil { 44 return err 45 } 46 47 segmentContainer, segmentPath := parseFullPath(headers["X-Object-Manifest"]) 48 if err := c.createDLOManifest(dstContainer, dstObjectName, segmentContainer+"/"+segmentPath, info.ContentType); err != nil { 49 return err 50 } 51 52 if err := c.ObjectDelete(srcContainer, srcObjectName); err != nil { 53 return err 54 } 55 56 return nil 57} 58 59// createDLOManifest creates a dynamic large object manifest 60func (c *Connection) createDLOManifest(container string, objectName string, prefix string, contentType string) error { 61 headers := make(Headers) 62 headers["X-Object-Manifest"] = prefix 63 manifest, err := c.ObjectCreate(container, objectName, false, "", contentType, headers) 64 if err != nil { 65 return err 66 } 67 68 if err := manifest.Close(); err != nil { 69 return err 70 } 71 72 return nil 73} 74 75// Close satisfies the io.Closer interface 76func (file *DynamicLargeObjectCreateFile) Close() error { 77 return file.Flush() 78} 79 80func (file *DynamicLargeObjectCreateFile) Flush() error { 81 err := file.conn.createDLOManifest(file.container, file.objectName, file.segmentContainer+"/"+file.prefix, file.contentType) 82 if err != nil { 83 return err 84 } 85 return file.conn.waitForSegmentsToShowUp(file.container, file.objectName, file.Size()) 86} 87 88func (c *Connection) getAllDLOSegments(segmentContainer, segmentPath string) ([]Object, error) { 89 //a simple container listing works 99.9% of the time 90 segments, err := c.ObjectsAll(segmentContainer, &ObjectsOpts{Prefix: segmentPath}) 91 if err != nil { 92 return nil, err 93 } 94 95 hasObjectName := make(map[string]struct{}) 96 for _, segment := range segments { 97 hasObjectName[segment.Name] = struct{}{} 98 } 99 100 //The container listing might be outdated (i.e. not contain all existing 101 //segment objects yet) because of temporary inconsistency (Swift is only 102 //eventually consistent!). Check its completeness. 103 segmentNumber := 0 104 for { 105 segmentNumber++ 106 segmentName := getSegment(segmentPath, segmentNumber) 107 if _, seen := hasObjectName[segmentName]; seen { 108 continue 109 } 110 111 //This segment is missing in the container listing. Use a more reliable 112 //request to check its existence. (HEAD requests on segments are 113 //guaranteed to return the correct metadata, except for the pathological 114 //case of an outage of large parts of the Swift cluster or its network, 115 //since every segment is only written once.) 116 segment, _, err := c.Object(segmentContainer, segmentName) 117 switch err { 118 case nil: 119 //found new segment -> add it in the correct position and keep 120 //going, more might be missing 121 if segmentNumber <= len(segments) { 122 segments = append(segments[:segmentNumber], segments[segmentNumber-1:]...) 123 segments[segmentNumber-1] = segment 124 } else { 125 segments = append(segments, segment) 126 } 127 continue 128 case ObjectNotFound: 129 //This segment is missing. Since we upload segments sequentially, 130 //there won't be any more segments after it. 131 return segments, nil 132 default: 133 return nil, err //unexpected error 134 } 135 } 136} 137