github.com/TeaOSLab/EdgeNode@v1.3.8/internal/caches/writer_partial_file.go (about) 1 // Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. 2 3 package caches 4 5 import ( 6 "encoding/binary" 7 fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs" 8 "github.com/iwind/TeaGo/types" 9 "io" 10 "sync" 11 ) 12 13 type PartialFileWriter struct { 14 rawWriter *fsutils.File 15 key string 16 17 metaHeaderSize int 18 headerSize int64 19 20 metaBodySize int64 21 bodySize int64 22 23 expiredAt int64 24 endFunc func() 25 once sync.Once 26 27 isNew bool 28 isPartial bool 29 bodyOffset int64 30 31 ranges *PartialRanges 32 rangePath string 33 } 34 35 func NewPartialFileWriter(rawWriter *fsutils.File, key string, expiredAt int64, metaHeaderSize int, metaBodySize int64, isNew bool, isPartial bool, bodyOffset int64, ranges *PartialRanges, endFunc func()) *PartialFileWriter { 36 return &PartialFileWriter{ 37 key: key, 38 rawWriter: rawWriter, 39 expiredAt: expiredAt, 40 endFunc: endFunc, 41 isNew: isNew, 42 isPartial: isPartial, 43 bodyOffset: bodyOffset, 44 ranges: ranges, 45 rangePath: PartialRangesFilePath(rawWriter.Name()), 46 metaHeaderSize: metaHeaderSize, 47 metaBodySize: metaBodySize, 48 } 49 } 50 51 // WriteHeader 写入数据 52 func (this *PartialFileWriter) WriteHeader(data []byte) (n int, err error) { 53 if !this.isNew { 54 return 55 } 56 n, err = this.rawWriter.Write(data) 57 this.headerSize += int64(n) 58 if err != nil { 59 _ = this.Discard() 60 } 61 return 62 } 63 64 func (this *PartialFileWriter) AppendHeader(data []byte) error { 65 _, err := this.rawWriter.Write(data) 66 if err != nil { 67 _ = this.Discard() 68 } else { 69 var c = len(data) 70 this.headerSize += int64(c) 71 err = this.WriteHeaderLength(int(this.headerSize)) 72 if err != nil { 73 _ = this.Discard() 74 } 75 } 76 return err 77 } 78 79 // WriteHeaderLength 写入Header长度数据 80 func (this *PartialFileWriter) WriteHeaderLength(headerLength int) error { 81 if this.metaHeaderSize > 0 && this.metaHeaderSize == headerLength { 82 return nil 83 } 84 85 var bytes4 = make([]byte, 4) 86 binary.BigEndian.PutUint32(bytes4, uint32(headerLength)) 87 _, err := this.rawWriter.Seek(SizeExpiresAt+SizeStatus+SizeURLLength, io.SeekStart) 88 if err != nil { 89 _ = this.Discard() 90 return err 91 } 92 _, err = this.rawWriter.Write(bytes4) 93 if err != nil { 94 _ = this.Discard() 95 return err 96 } 97 return nil 98 } 99 100 // Write 写入数据 101 func (this *PartialFileWriter) Write(data []byte) (n int, err error) { 102 n, err = this.rawWriter.Write(data) 103 this.bodySize += int64(n) 104 if err != nil { 105 _ = this.Discard() 106 } 107 return 108 } 109 110 // WriteAt 在指定位置写入数据 111 func (this *PartialFileWriter) WriteAt(offset int64, data []byte) error { 112 var c = int64(len(data)) 113 if c == 0 { 114 return nil 115 } 116 var end = offset + c - 1 117 118 // 是否已包含在内 119 if this.ranges.Contains(offset, end) { 120 return nil 121 } 122 123 // prevent extending too much space in a single writing 124 var maxOffset = this.ranges.Max() 125 if offset-maxOffset > 16<<20 { 126 var extendSizePerStep int64 = 1 << 20 127 var maxExtendSize int64 = 32 << 20 128 if fsutils.DiskIsExtremelyFast() { 129 maxExtendSize = 128 << 20 130 extendSizePerStep = 4 << 20 131 } else if fsutils.DiskIsFast() { 132 maxExtendSize = 64 << 20 133 extendSizePerStep = 2 << 20 134 } 135 if offset-maxOffset > maxExtendSize { 136 stat, err := this.rawWriter.Stat() 137 if err != nil { 138 return nil 139 } 140 141 // extend min size to prepare for file tail 142 if stat.Size()+extendSizePerStep <= this.bodyOffset+offset+int64(len(data)) { 143 _ = this.rawWriter.Truncate(stat.Size() + extendSizePerStep) 144 return nil 145 } 146 } 147 } 148 149 if this.bodyOffset == 0 { 150 var keyLength = 0 151 if this.ranges.Version == 0 { // 以往的版本包含有Key 152 keyLength = len(this.key) 153 } 154 this.bodyOffset = SizeMeta + int64(keyLength) + this.headerSize 155 } 156 157 _, err := this.rawWriter.WriteAt(data, this.bodyOffset+offset) 158 if err != nil { 159 return err 160 } 161 162 this.ranges.Add(offset, end) 163 164 return nil 165 } 166 167 // SetBodyLength 设置内容总长度 168 func (this *PartialFileWriter) SetBodyLength(bodyLength int64) { 169 this.bodySize = bodyLength 170 } 171 172 // WriteBodyLength 写入Body长度数据 173 func (this *PartialFileWriter) WriteBodyLength(bodyLength int64) error { 174 if this.metaBodySize > 0 && this.metaBodySize == bodyLength { 175 return nil 176 } 177 var bytes8 = make([]byte, 8) 178 binary.BigEndian.PutUint64(bytes8, uint64(bodyLength)) 179 _, err := this.rawWriter.Seek(SizeExpiresAt+SizeStatus+SizeURLLength+SizeHeaderLength, io.SeekStart) 180 if err != nil { 181 _ = this.Discard() 182 return err 183 } 184 _, err = this.rawWriter.Write(bytes8) 185 if err != nil { 186 _ = this.Discard() 187 return err 188 } 189 return nil 190 } 191 192 // Close 关闭 193 func (this *PartialFileWriter) Close() error { 194 defer this.once.Do(func() { 195 this.endFunc() 196 }) 197 198 this.ranges.BodySize = this.bodySize 199 err := this.ranges.WriteToFile(this.rangePath) 200 if err != nil { 201 _ = this.rawWriter.Close() 202 this.remove() 203 return err 204 } 205 206 // 关闭当前writer 207 if this.isNew { 208 err = this.WriteHeaderLength(types.Int(this.headerSize)) 209 if err != nil { 210 _ = this.rawWriter.Close() 211 this.remove() 212 return err 213 } 214 err = this.WriteBodyLength(this.bodySize) 215 if err != nil { 216 _ = this.rawWriter.Close() 217 this.remove() 218 return err 219 } 220 } 221 222 err = this.rawWriter.Close() 223 if err != nil { 224 this.remove() 225 } 226 227 return err 228 } 229 230 // Discard 丢弃 231 func (this *PartialFileWriter) Discard() error { 232 defer this.once.Do(func() { 233 this.endFunc() 234 }) 235 236 _ = this.rawWriter.Close() 237 238 SharedPartialRangesQueue.Delete(this.rangePath) 239 240 _ = fsutils.Remove(this.rangePath) 241 242 err := fsutils.Remove(this.rawWriter.Name()) 243 244 return err 245 } 246 247 func (this *PartialFileWriter) HeaderSize() int64 { 248 return this.headerSize 249 } 250 251 func (this *PartialFileWriter) BodySize() int64 { 252 return this.bodySize 253 } 254 255 func (this *PartialFileWriter) ExpiredAt() int64 { 256 return this.expiredAt 257 } 258 259 func (this *PartialFileWriter) Key() string { 260 return this.key 261 } 262 263 // ItemType 获取内容类型 264 func (this *PartialFileWriter) ItemType() ItemType { 265 return ItemTypeFile 266 } 267 268 func (this *PartialFileWriter) IsNew() bool { 269 return this.isNew && len(this.ranges.Ranges) == 0 270 } 271 272 func (this *PartialFileWriter) remove() { 273 _ = fsutils.Remove(this.rawWriter.Name()) 274 275 SharedPartialRangesQueue.Delete(this.rangePath) 276 277 _ = fsutils.Remove(this.rangePath) 278 }